From f81413112c2858e44dc308c056c38f8fbc5ed94f Mon Sep 17 00:00:00 2001 From: Artem Babak Date: Wed, 26 May 2021 21:02:35 +0300 Subject: [PATCH 001/151] Edge: menu order changes --- ui-ngx/src/app/core/services/menu.service.ts | 32 +++++++++---------- .../pages/customer/customer-routing.module.ts | 2 +- .../customers-table-config.resolver.ts | 2 +- .../dashboards-table-config.resolver.ts | 2 +- .../home/pages/edge/edge-routing.module.ts | 26 +++++++++++++-- .../pages/edge/edges-table-config.resolver.ts | 2 +- .../rulechain/rule-node-details.component.ts | 2 +- .../rulechain/rulechain-page.component.ts | 2 +- .../rulechains-table-config.resolver.ts | 6 ++-- .../pages/rulechain/rulenode.component.ts | 2 +- 10 files changed, 49 insertions(+), 29 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 07a6d8f237..10dedd4fc7 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -291,26 +291,26 @@ export class MenuService { ); if (authState.edgesSupportEnabled) { sections.push( + { + id: guid(), + name: 'edge.edge-instances', + type: 'link', + path: '/edgeInstances', + icon: 'router' + }, { id: guid(), name: 'edge.management', type: 'toggle', - path: '/edges', - height: '80px', - icon: 'router', + path: '/edgeManagement', + height: '40px', + icon: 'settings_input_antenna', pages: [ - { - id: guid(), - name: 'edge.edge-instances', - type: 'link', - path: '/edges', - icon: 'router' - }, { id: guid(), name: 'edge.rulechain-templates', type: 'link', - path: '/edges/ruleChains', + path: '/edgeManagement/ruleChains', icon: 'settings_ethernet' } ] @@ -448,12 +448,12 @@ export class MenuService { { name: 'edge.edge-instances', icon: 'router', - path: '/edges' + path: '/edgeInstances' }, { name: 'edge.rulechain-templates', icon: 'settings_ethernet', - path: '/edges/ruleChains' + path: '/edgeManagement/ruleChains' } ] } @@ -548,7 +548,7 @@ export class MenuService { id: guid(), name: 'edge.edge-instances', type: 'link', - path: '/edges', + path: '/edgeInstances', icon: 'router' } ); @@ -606,8 +606,8 @@ export class MenuService { places: [ { name: 'edge.edge-instances', - icon: 'router', - path: '/edges' + icon: 'settings_input_antenna', + path: '/edgeInstances' } ] } diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts index f5f3ee05fa..2ed306c2d8 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts @@ -98,7 +98,7 @@ const routes: Routes = [ } }, { - path: ':customerId/edges', + path: ':customerId/edgeInstances', component: EntitiesTableComponent, data: { auth: [Authority.TENANT_ADMIN], diff --git a/ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts index 8ac9e64390..5a6e03b3d7 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/customer/customers-table-config.resolver.ts @@ -169,7 +169,7 @@ export class CustomersTableConfigResolver implements Resolve): boolean { diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts index c572fa958f..a4c17e75ac 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts @@ -362,7 +362,7 @@ export class DashboardsTableConfigResolver implements Resolve) { diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts index 00cd38b9c4..d5a3fcc8a0 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts @@ -152,7 +152,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O } if (this.ruleNode.targetRuleChainId) { if (this.ruleChainType === RuleChainType.EDGE) { - this.router.navigateByUrl(`/edges/ruleChains/${this.ruleNode.targetRuleChainId}`); + this.router.navigateByUrl(`/edgeManagement/ruleChains/${this.ruleNode.targetRuleChainId}`); } else { this.router.navigateByUrl(`/ruleChains/${this.ruleNode.targetRuleChainId}`); } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts index 54102ffbcd..ea9cc54747 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts @@ -1289,7 +1289,7 @@ export class RuleChainPageComponent extends PageComponent if (this.ruleChainType !== RuleChainType.EDGE) { this.router.navigateByUrl(`ruleChains/${this.ruleChain.id.id}`); } else { - this.router.navigateByUrl(`edges/ruleChains/${this.ruleChain.id.id}`); + this.router.navigateByUrl(`edgeManagement/ruleChains/${this.ruleChain.id.id}`); } } else { this.createRuleChainModel(); diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts index 37a21cab2e..ffc05052fe 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts @@ -285,7 +285,7 @@ export class RuleChainsTableConfigResolver implements Resolve Date: Thu, 27 May 2021 09:39:53 +0300 Subject: [PATCH 002/151] Minor code cleanup --- .../src/app/modules/home/pages/edge/edge-routing.module.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts index bd96b953a8..4ffa51a6bd 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts @@ -39,7 +39,6 @@ import { RuleNodeComponentsResolver, TooltipsterResolver } from '@home/pages/rulechain/rulechain-routing.module'; -import { CustomersTableConfigResolver } from '@home/pages/customer/customers-table-config.resolver'; const routes: Routes = [ { @@ -56,7 +55,7 @@ const routes: Routes = [ component: EntitiesTableComponent, data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], - edgesType: 'tenant', + edgesType: 'tenant' }, resolve: { entitiesTableConfig: EdgesTableConfigResolver @@ -272,8 +271,7 @@ const routes: Routes = [ ] } ] - } - ]; + }]; @NgModule({ imports: [RouterModule.forChild(routes)], From 0c886656543987f27d59c2c053c11677de147a8b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 25 May 2021 19:44:34 +0300 Subject: [PATCH 003/151] js-executor: scriptMap refactored from Object to the Map() --- .../api/jsInvokeMessageProcessor.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index 2964291a30..a760e21e47 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -34,10 +34,9 @@ const slowQueryLogBody = config.get('script.slow_query_log_body') === 'true'; const {performance} = require('perf_hooks'); function JsInvokeMessageProcessor(producer) { - console.log("Producer:", producer); this.producer = producer; this.executor = new JsExecutor(useSandbox); - this.scriptMap = {}; + this.scriptMap = new Map(); this.scriptIds = []; this.executedScriptsCounter = 0; } @@ -157,12 +156,12 @@ JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, re JsInvokeMessageProcessor.prototype.processReleaseRequest = function(requestId, responseTopic, headers, releaseRequest) { var scriptId = getScriptId(releaseRequest); logger.debug('[%s] Processing release request, scriptId: [%s]', requestId, scriptId); - if (this.scriptMap[scriptId]) { + if (this.scriptMap.has(scriptId)) { var index = this.scriptIds.indexOf(scriptId); if (index > -1) { this.scriptIds.splice(index, 1); } - delete this.scriptMap[scriptId]; + this.scriptMap.delete(scriptId); } var releaseResponse = createReleaseResponse(scriptId, true); logger.debug('[%s] Sending success release response, scriptId: [%s]', requestId, scriptId); @@ -189,8 +188,8 @@ JsInvokeMessageProcessor.prototype.sendResponse = function (requestId, responseT JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scriptBody) { var self = this; return new Promise(function(resolve, reject) { - if (self.scriptMap[scriptId]) { - resolve(self.scriptMap[scriptId]); + if (self.scriptMap.has(scriptId)) { + resolve(self.scriptMap.get(scriptId)); } else { self.executor.compileScript(scriptBody).then( (script) => { @@ -206,16 +205,17 @@ JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scrip } JsInvokeMessageProcessor.prototype.cacheScript = function(scriptId, script) { - if (!this.scriptMap[scriptId]) { + if (!this.scriptMap.has(scriptId)) { this.scriptIds.push(scriptId); while (this.scriptIds.length > maxActiveScripts) { logger.info('Active scripts count [%s] exceeds maximum limit [%s]', this.scriptIds.length, maxActiveScripts); const prevScriptId = this.scriptIds.shift(); logger.info('Removing active script with id [%s]', prevScriptId); - delete this.scriptMap[prevScriptId]; + this.scriptMap.delete(prevScriptId); } } - this.scriptMap[scriptId] = script; + this.scriptMap.set(scriptId, script); + logger.info("scriptMap size is [%s]", this.scriptMap.size); } function createRemoteResponse(requestId, compileResponse, invokeResponse, releaseResponse) { From d729d9ee959963b570aeeaf27787cdd6b6f44bdf Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 27 May 2021 14:50:42 +0300 Subject: [PATCH 004/151] js-executor: instrumentation event for producer and consumer to define the exact flow how to Kafka works without batches (for debug only) --- msa/js-executor/queue/kafkaTemplate.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 2f4fd8751d..1fd21185d0 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -114,6 +114,30 @@ function KafkaProducer() { consumer = kafkaClient.consumer({groupId: 'js-executor-group'}); producer = kafkaClient.producer(); + + const { CONNECT } = producer.events; + const removeListenerC = producer.on(CONNECT, e => logger.info(`producer CONNECT`)); + const { DISCONNECT } = producer.events; + const removeListenerD = producer.on(DISCONNECT, e => logger.info(`producer DISCONNECT`)); + const { REQUEST } = producer.events; + const removeListenerR = producer.on(REQUEST, e => logger.info(`producer REQUEST ${e.payload.broker}`)); + const { REQUEST_TIMEOUT } = producer.events; + const removeListenerRT = producer.on(REQUEST_TIMEOUT, e => logger.info(`producer REQUEST_TIMEOUT ${e.payload.broker}`)); + const { REQUEST_QUEUE_SIZE } = producer.events; + const removeListenerRQS = producer.on(REQUEST_QUEUE_SIZE, e => logger.info(`producer REQUEST_QUEUE_SIZE ${e.payload.broker} size ${e.queueSize}`)); + + const removeListeners = {} + const { FETCH_START } = consumer.events; + removeListeners[FETCH_START] = consumer.on(FETCH_START, e => logger.info(`consumer FETCH_START`)); + const { FETCH } = consumer.events; + removeListeners[FETCH] = consumer.on(FETCH, e => logger.info(`consumer FETCH numberOfBatches ${e.payload.numberOfBatches} duration ${e.payload.duration}`)); + const { START_BATCH_PROCESS } = consumer.events; + removeListeners[START_BATCH_PROCESS] = consumer.on(START_BATCH_PROCESS, e => logger.info(`consumer START_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); + const { END_BATCH_PROCESS } = consumer.events; + removeListeners[END_BATCH_PROCESS] = consumer.on(END_BATCH_PROCESS, e => logger.info(`consumer END_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); + const { COMMIT_OFFSETS } = consumer.events; + removeListeners[COMMIT_OFFSETS] = consumer.on(COMMIT_OFFSETS, e => logger.info(`consumer COMMIT_OFFSETS topics ${e.payload.topics}`)); + const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); await consumer.connect(); await producer.connect(); From 35e2ff99c3d3f7a488fdc4d3fb3375daa06d41f9 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 27 May 2021 14:55:10 +0300 Subject: [PATCH 005/151] js-executor: send messages as batch --- .../api/jsInvokeMessageProcessor.js | 25 ++++---- msa/js-executor/queue/kafkaTemplate.js | 63 ++++++++++++++----- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index a760e21e47..867dd82b39 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -172,17 +172,20 @@ JsInvokeMessageProcessor.prototype.sendResponse = function (requestId, responseT var tStartSending = performance.now(); var remoteResponse = createRemoteResponse(requestId, compileResponse, invokeResponse, releaseResponse); var rawResponse = Buffer.from(JSON.stringify(remoteResponse), 'utf8'); - this.producer.send(responseTopic, scriptId, rawResponse, headers).then( - () => { - logger.debug('[%s] Response sent to queue, took [%s]ms, scriptId: [%s]', requestId, (performance.now() - tStartSending), scriptId); - }, - (err) => { - if (err) { - logger.error('[%s] Failed to send response to queue: %s', requestId, err.message); - logger.error(err.stack); - } - } - ); + logger.debug('[%s] Sending response to queue, scriptId: [%s]', requestId, scriptId); + this.producer.send(responseTopic, scriptId, rawResponse, headers); +//TODO put error msg for other queues implementation except Kafka +// .then( +// () => { +// logger.info('[%s] Response sent to queue, took [%s]ms, scriptId: [%s]', requestId, (performance.now() - tStartSending), scriptId); +// }, +// (err) => { +// if (err) { +// logger.error('[%s] Failed to send response to queue: %s', requestId, err.message); +// logger.error(err.stack); +// } +// } +// ); } JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scriptBody) { diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 1fd21185d0..da46a45301 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -33,24 +33,54 @@ let producer; const configEntries = []; +let topicMessages = []; +let loopSend; + function KafkaProducer() { - this.send = async (responseTopic, scriptId, rawResponse, headers) => { - return producer.send( - { - topic: responseTopic, - acks: acks, - compression: compressionType, - messages: [ - { - key: scriptId, - value: rawResponse, - headers: headers.data - } - ] - }); + this.send = (responseTopic, scriptId, rawResponse, headers) => { + logger.debug('Pending queue response, scriptId: [%s]', scriptId); + const message = { + topic: responseTopic, + messages: [{ + key: scriptId, + value: rawResponse, + headers: headers.data + }] + }; + + topicMessages.push(message); + return {}; } } +function sendLoopFunction() { + loopSend = setInterval(sendProducerMsg, 200); +} + +function sendProducerMsg() { + if (topicMessages.length > 0) { + logger.info('sendProducerMsg from queue response, lenght: [%s]', topicMessages.length ); + const messagesToSend = topicMessages; + topicMessages = []; + producer.sendBatch({ + topicMessages: messagesToSend, + acks: acks, + compression: compressionType + }).then( + () => { + logger.info('Response sent to kafka, length: [%s]', messagesToSend.length ); + }, + (err) => { + if (err) { + logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length ); + topicMessages = messagesToSend.concat(topicMessages); + logger.error(err.stack); + } + } + ); + } +} + (async () => { try { logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); @@ -141,6 +171,7 @@ function KafkaProducer() { const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); await consumer.connect(); await producer.connect(); + sendLoopFunction(); await consumer.subscribe({topic: requestTopic}); logger.info('Started ThingsBoard JavaScript Executor Microservice.'); @@ -221,6 +252,10 @@ async function disconnectProducer() { var _producer = producer; producer = null; try { + logger.info('Stopping loop...'); + //TODO: send handle msg + clearInterval(loopSend); + sendProducerMsg(); await _producer.disconnect(); logger.info('Kafka Producer stopped.'); } catch (e) { From 0970ce65b448f622056c070831aa1625f1e81f36 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 27 May 2021 18:45:29 +0300 Subject: [PATCH 006/151] js-executor: maxBatchSize --- msa/js-executor/queue/kafkaTemplate.js | 88 ++++++++++++++++---------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index da46a45301..a061215b28 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -26,6 +26,9 @@ const acks = Number(config.get('kafka.acks')); const requestTimeout = Number(config.get('kafka.requestTimeout')); const compressionType = (config.get('kafka.requestTimeout') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; +const linger = 5; //milliseconds //TODO move to the config +const maxBatchSize = 10; //max messages in batch //TODO move to the config + let kafkaClient; let kafkaAdmin; let consumer; @@ -33,8 +36,8 @@ let producer; const configEntries = []; -let topicMessages = []; -let loopSend; +let batchMessages = []; +let sendLoopInstance; function KafkaProducer() { this.send = (responseTopic, scriptId, rawResponse, headers) => { @@ -48,36 +51,51 @@ function KafkaProducer() { }] }; - topicMessages.push(message); + pushMessageToSendLater(message); return {}; } } -function sendLoopFunction() { - loopSend = setInterval(sendProducerMsg, 200); +function pushMessageToSendLater(message) { + batchMessages.push(message); + if (batchMessages.length >= maxBatchSize) { + sendMessagesAsBatch(); + sendLoopWithLinger(); //reset loop function and reschedule new linger + } +} + +function sendLoopWithLinger() { + if (sendLoopInstance) { + logger.debug("Clear sendLoop scheduler. Starting new send loop with linger [%s]", linger); + clearInterval(sendLoopInstance); + } else { + logger.debug("Starting new send loop with linger [%s]", linger) + } + sendLoopInstance = setInterval(sendMessagesAsBatch, linger); } -function sendProducerMsg() { - if (topicMessages.length > 0) { - logger.info('sendProducerMsg from queue response, lenght: [%s]', topicMessages.length ); - const messagesToSend = topicMessages; - topicMessages = []; +function sendMessagesAsBatch() { + if (batchMessages.length > 0) { + logger.info('sendMessagesAsBatch, lenght: [%s]', batchMessages.length ); + const messagesToSend = batchMessages; + batchMessages = []; producer.sendBatch({ topicMessages: messagesToSend, acks: acks, compression: compressionType }).then( - () => { - logger.info('Response sent to kafka, length: [%s]', messagesToSend.length ); - }, - (err) => { - if (err) { - logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length ); - topicMessages = messagesToSend.concat(topicMessages); - logger.error(err.stack); - } - } - ); + () => { + logger.info('Response sent to kafka, length: [%s]', messagesToSend.length ); + }, + (err) => { + logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length ); + batchMessages = messagesToSend.concat(batchMessages); + logger.error(err.stack); + } + ); + + } else { + //logger.debug("nothing to send"); } } @@ -156,22 +174,22 @@ function sendProducerMsg() { const { REQUEST_QUEUE_SIZE } = producer.events; const removeListenerRQS = producer.on(REQUEST_QUEUE_SIZE, e => logger.info(`producer REQUEST_QUEUE_SIZE ${e.payload.broker} size ${e.queueSize}`)); - const removeListeners = {} - const { FETCH_START } = consumer.events; - removeListeners[FETCH_START] = consumer.on(FETCH_START, e => logger.info(`consumer FETCH_START`)); - const { FETCH } = consumer.events; - removeListeners[FETCH] = consumer.on(FETCH, e => logger.info(`consumer FETCH numberOfBatches ${e.payload.numberOfBatches} duration ${e.payload.duration}`)); - const { START_BATCH_PROCESS } = consumer.events; - removeListeners[START_BATCH_PROCESS] = consumer.on(START_BATCH_PROCESS, e => logger.info(`consumer START_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); - const { END_BATCH_PROCESS } = consumer.events; - removeListeners[END_BATCH_PROCESS] = consumer.on(END_BATCH_PROCESS, e => logger.info(`consumer END_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); - const { COMMIT_OFFSETS } = consumer.events; - removeListeners[COMMIT_OFFSETS] = consumer.on(COMMIT_OFFSETS, e => logger.info(`consumer COMMIT_OFFSETS topics ${e.payload.topics}`)); +// const removeListeners = {} +// const { FETCH_START } = consumer.events; +// removeListeners[FETCH_START] = consumer.on(FETCH_START, e => logger.info(`consumer FETCH_START`)); +// const { FETCH } = consumer.events; +// removeListeners[FETCH] = consumer.on(FETCH, e => logger.info(`consumer FETCH numberOfBatches ${e.payload.numberOfBatches} duration ${e.payload.duration}`)); +// const { START_BATCH_PROCESS } = consumer.events; +// removeListeners[START_BATCH_PROCESS] = consumer.on(START_BATCH_PROCESS, e => logger.info(`consumer START_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); +// const { END_BATCH_PROCESS } = consumer.events; +// removeListeners[END_BATCH_PROCESS] = consumer.on(END_BATCH_PROCESS, e => logger.info(`consumer END_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); +// const { COMMIT_OFFSETS } = consumer.events; +// removeListeners[COMMIT_OFFSETS] = consumer.on(COMMIT_OFFSETS, e => logger.info(`consumer COMMIT_OFFSETS topics ${e.payload.topics}`)); const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); await consumer.connect(); await producer.connect(); - sendLoopFunction(); + sendLoopWithLinger(); await consumer.subscribe({topic: requestTopic}); logger.info('Started ThingsBoard JavaScript Executor Microservice.'); @@ -254,8 +272,8 @@ async function disconnectProducer() { try { logger.info('Stopping loop...'); //TODO: send handle msg - clearInterval(loopSend); - sendProducerMsg(); + clearInterval(sendLoopInstance); + sendMessagesAsBatch(); await _producer.disconnect(); logger.info('Kafka Producer stopped.'); } catch (e) { From 93bea7020584343511b1a561bb861fd010df037e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 28 May 2021 09:41:35 +0300 Subject: [PATCH 007/151] js-executor: added parameters for producer TB_KAFKA_BATCH_SIZE and TB_KAFKA_LINGER_MS; added print stats frequency SCRIPT_STAT_PRINT_FREQUENCY --- .../api/jsInvokeMessageProcessor.js | 17 +++++--- .../config/custom-environment-variables.yml | 3 ++ msa/js-executor/config/default.yml | 7 ++- msa/js-executor/queue/kafkaTemplate.js | 43 +++++++++++-------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index 867dd82b39..dec935c8ad 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -25,6 +25,7 @@ const config = require('config'), Utils = require('./utils'), JsExecutor = require('./jsExecutor'); +const statFrequency = Number(config.get('script.stat_print_frequency')); const scriptBodyTraceFrequency = Number(config.get('script.script_body_trace_frequency')); const useSandbox = config.get('script.use_sandbox') === 'true'; const maxActiveScripts = Number(config.get('script.max_active_scripts')); @@ -39,6 +40,7 @@ function JsInvokeMessageProcessor(producer) { this.scriptMap = new Map(); this.scriptIds = []; this.executedScriptsCounter = 0; + this.lastStatTime = performance.now(); } JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) { @@ -118,11 +120,16 @@ JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, re var scriptId = getScriptId(invokeRequest); logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId); this.executedScriptsCounter++; - if ( this.executedScriptsCounter >= scriptBodyTraceFrequency ) { - this.executedScriptsCounter = 0; - if (logger.levels[logger.level] >= logger.levels['debug']) { - logger.debug('[%s] Executing script body: [%s]', scriptId, invokeRequest.scriptBody); - } + if (this.executedScriptsCounter % statFrequency == 0) { + var nowMs = performance.now(); + var msSinceLastStat = nowMs - this.lastStatTime; + var requestsPerSec = statFrequency / msSinceLastStat * 1000; //msSinceLastStat can't be zero in the real world + this.lastStatTime = nowMs; + logger.info('STAT[%s]: requests [%s], took [%s]ms, request/s [%s]', this.executedScriptsCounter, statFrequency, msSinceLastStat, requestsPerSec); + } + + if (this.executedScriptsCounter % scriptBodyTraceFrequency == 0) { + logger.info('[%s] Executing script body: [%s]', scriptId, invokeRequest.scriptBody); } this.getOrCompileScript(scriptId, invokeRequest.scriptBody).then( (script) => { diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 5e2bfe8342..c5eb15e8b9 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -26,6 +26,8 @@ kafka: servers: "TB_KAFKA_SERVERS" replication_factor: "TB_QUEUE_KAFKA_REPLICATION_FACTOR" acks: "TB_KAFKA_ACKS" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge + batch_size: "${TB_KAFKA_BATCH_SIZE:128}" # for producer + linger_ms: "${TB_KAFKA_LINGER_MS:1}" # for producer requestTimeout: "TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS" compression: "TB_QUEUE_KAFKA_COMPRESSION" # gzip or uncompressed topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" @@ -70,6 +72,7 @@ logger: script: use_sandbox: "SCRIPT_USE_SANDBOX" + stat_print_frequency: "SCRIPT_STAT_PRINT_FREQUENCY" script_body_trace_frequency: "SCRIPT_BODY_TRACE_FREQUENCY" max_active_scripts: "MAX_ACTIVE_SCRIPTS" slow_query_log_ms: "SLOW_QUERY_LOG_MS" #1.123456 diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index cdf5b35421..5a2c47a675 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -26,6 +26,8 @@ kafka: servers: "localhost:9092" replication_factor: "1" acks: "1" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge + batch_size: "128" # for producer + linger_ms: "1" # for producer requestTimeout: "30000" # The default value in kafkajs is: 30000 compression: "gzip" # gzip or uncompressed topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1" @@ -59,7 +61,8 @@ logger: script: use_sandbox: "true" - script_body_trace_frequency: "1000" + script_body_trace_frequency: "10000" + stat_print_frequency: "10000" max_active_scripts: "1000" - slow_query_log_ms: "1.000000" #millis + slow_query_log_ms: "5.000000" #millis slow_query_log_body: "false" diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index a061215b28..79d87c17b4 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -23,12 +23,11 @@ const replicationFactor = Number(config.get('kafka.replication_factor')); const topicProperties = config.get('kafka.topic_properties'); const kafkaClientId = config.get('kafka.client_id'); const acks = Number(config.get('kafka.acks')); +const maxBatchSize = Number(config.get('kafka.batch_size')); +const linger = Number(config.get('kafka.linger_ms')); const requestTimeout = Number(config.get('kafka.requestTimeout')); const compressionType = (config.get('kafka.requestTimeout') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; -const linger = 5; //milliseconds //TODO move to the config -const maxBatchSize = 10; //max messages in batch //TODO move to the config - let kafkaClient; let kafkaAdmin; let consumer; @@ -76,7 +75,9 @@ function sendLoopWithLinger() { function sendMessagesAsBatch() { if (batchMessages.length > 0) { - logger.info('sendMessagesAsBatch, lenght: [%s]', batchMessages.length ); + if (batchMessages.length > 1) { + logger.info('sendMessagesAsBatch, length: [%s]', batchMessages.length); + } const messagesToSend = batchMessages; batchMessages = []; producer.sendBatch({ @@ -85,17 +86,15 @@ function sendMessagesAsBatch() { compression: compressionType }).then( () => { - logger.info('Response sent to kafka, length: [%s]', messagesToSend.length ); + logger.debug('Response sent to kafka, length: [%s]', messagesToSend.length); }, (err) => { - logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length ); + logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length); batchMessages = messagesToSend.concat(batchMessages); logger.error(err.stack); } ); - } else { - //logger.debug("nothing to send"); } } @@ -163,6 +162,8 @@ function sendMessagesAsBatch() { consumer = kafkaClient.consumer({groupId: 'js-executor-group'}); producer = kafkaClient.producer(); +/* + //producer event instrumentation to debug const { CONNECT } = producer.events; const removeListenerC = producer.on(CONNECT, e => logger.info(`producer CONNECT`)); const { DISCONNECT } = producer.events; @@ -173,18 +174,22 @@ function sendMessagesAsBatch() { const removeListenerRT = producer.on(REQUEST_TIMEOUT, e => logger.info(`producer REQUEST_TIMEOUT ${e.payload.broker}`)); const { REQUEST_QUEUE_SIZE } = producer.events; const removeListenerRQS = producer.on(REQUEST_QUEUE_SIZE, e => logger.info(`producer REQUEST_QUEUE_SIZE ${e.payload.broker} size ${e.queueSize}`)); +*/ -// const removeListeners = {} -// const { FETCH_START } = consumer.events; -// removeListeners[FETCH_START] = consumer.on(FETCH_START, e => logger.info(`consumer FETCH_START`)); -// const { FETCH } = consumer.events; -// removeListeners[FETCH] = consumer.on(FETCH, e => logger.info(`consumer FETCH numberOfBatches ${e.payload.numberOfBatches} duration ${e.payload.duration}`)); -// const { START_BATCH_PROCESS } = consumer.events; -// removeListeners[START_BATCH_PROCESS] = consumer.on(START_BATCH_PROCESS, e => logger.info(`consumer START_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); -// const { END_BATCH_PROCESS } = consumer.events; -// removeListeners[END_BATCH_PROCESS] = consumer.on(END_BATCH_PROCESS, e => logger.info(`consumer END_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); -// const { COMMIT_OFFSETS } = consumer.events; -// removeListeners[COMMIT_OFFSETS] = consumer.on(COMMIT_OFFSETS, e => logger.info(`consumer COMMIT_OFFSETS topics ${e.payload.topics}`)); +/* + //consumer event instrumentation to debug + const removeListeners = {} + const { FETCH_START } = consumer.events; + removeListeners[FETCH_START] = consumer.on(FETCH_START, e => logger.info(`consumer FETCH_START`)); + const { FETCH } = consumer.events; + removeListeners[FETCH] = consumer.on(FETCH, e => logger.info(`consumer FETCH numberOfBatches ${e.payload.numberOfBatches} duration ${e.payload.duration}`)); + const { START_BATCH_PROCESS } = consumer.events; + removeListeners[START_BATCH_PROCESS] = consumer.on(START_BATCH_PROCESS, e => logger.info(`consumer START_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); + const { END_BATCH_PROCESS } = consumer.events; + removeListeners[END_BATCH_PROCESS] = consumer.on(END_BATCH_PROCESS, e => logger.info(`consumer END_BATCH_PROCESS topic ${e.payload.topic} batchSize ${e.payload.batchSize}`)); + const { COMMIT_OFFSETS } = consumer.events; + removeListeners[COMMIT_OFFSETS] = consumer.on(COMMIT_OFFSETS, e => logger.info(`consumer COMMIT_OFFSETS topics ${e.payload.topics}`)); +*/ const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); await consumer.connect(); From 2cba1e2f16e9a1a438d2e4bf7af0afa8968fbae5 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 28 May 2021 18:07:31 +0300 Subject: [PATCH 008/151] js-executor reduced log severity to debug --- msa/js-executor/queue/kafkaTemplate.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 79d87c17b4..f467d0813b 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -75,9 +75,7 @@ function sendLoopWithLinger() { function sendMessagesAsBatch() { if (batchMessages.length > 0) { - if (batchMessages.length > 1) { - logger.info('sendMessagesAsBatch, length: [%s]', batchMessages.length); - } + logger.debug('sendMessagesAsBatch, length: [%s]', batchMessages.length); const messagesToSend = batchMessages; batchMessages = []; producer.sendBatch({ From a4e28ad94533610c4de0f855bad75dcd4460ac74 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 1 Jun 2021 16:00:32 +0300 Subject: [PATCH 009/151] js-executor fixed promises for each message for Kafka batches --- .../api/jsInvokeMessageProcessor.js | 24 +++++++------- msa/js-executor/queue/kafkaTemplate.js | 33 ++++++++++++------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index dec935c8ad..1aabd216d2 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -180,19 +180,17 @@ JsInvokeMessageProcessor.prototype.sendResponse = function (requestId, responseT var remoteResponse = createRemoteResponse(requestId, compileResponse, invokeResponse, releaseResponse); var rawResponse = Buffer.from(JSON.stringify(remoteResponse), 'utf8'); logger.debug('[%s] Sending response to queue, scriptId: [%s]', requestId, scriptId); - this.producer.send(responseTopic, scriptId, rawResponse, headers); -//TODO put error msg for other queues implementation except Kafka -// .then( -// () => { -// logger.info('[%s] Response sent to queue, took [%s]ms, scriptId: [%s]', requestId, (performance.now() - tStartSending), scriptId); -// }, -// (err) => { -// if (err) { -// logger.error('[%s] Failed to send response to queue: %s', requestId, err.message); -// logger.error(err.stack); -// } -// } -// ); + this.producer.send(responseTopic, scriptId, rawResponse, headers).then( + () => { + logger.debug('[%s] Response sent to queue, took [%s]ms, scriptId: [%s]', requestId, (performance.now() - tStartSending), scriptId); + }, + (err) => { + if (err) { + logger.error('[%s] Failed to send response to queue: %s', requestId, err.message); + logger.error(err.stack); + } + } + ); } JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scriptBody) { diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index f467d0813b..1fac350976 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -36,11 +36,12 @@ let producer; const configEntries = []; let batchMessages = []; +let batchResolvers = []; let sendLoopInstance; function KafkaProducer() { this.send = (responseTopic, scriptId, rawResponse, headers) => { - logger.debug('Pending queue response, scriptId: [%s]', scriptId); + logger.debug('Pending queue response, scriptId: [%s]', scriptId); const message = { topic: responseTopic, messages: [{ @@ -50,17 +51,22 @@ function KafkaProducer() { }] }; - pushMessageToSendLater(message); - return {}; + return pushMessageToSendLater(message); } } function pushMessageToSendLater(message) { + let resolver; + const promise = new Promise((resolve, reject) => { + resolver = resolve; + }); batchMessages.push(message); + batchResolvers.push(resolver); if (batchMessages.length >= maxBatchSize) { sendMessagesAsBatch(); sendLoopWithLinger(); //reset loop function and reschedule new linger } + return promise; } function sendLoopWithLinger() { @@ -77,22 +83,27 @@ function sendMessagesAsBatch() { if (batchMessages.length > 0) { logger.debug('sendMessagesAsBatch, length: [%s]', batchMessages.length); const messagesToSend = batchMessages; + const resolvers = batchResolvers; batchMessages = []; + batchResolvers = []; producer.sendBatch({ topicMessages: messagesToSend, acks: acks, compression: compressionType }).then( - () => { - logger.debug('Response sent to kafka, length: [%s]', messagesToSend.length); - }, - (err) => { - logger.error('Failed to send kafka, length: [%s], pending to reprocess msgs', messagesToSend.length); - batchMessages = messagesToSend.concat(batchMessages); - logger.error(err.stack); + () => { + logger.debug('Response batch sent to kafka, length: [%s]', messagesToSend.length); + for (let i = 0; i < promisesToSend.length; i++) { + resolvers[i](); } + }, + (err) => { + logger.error('Failed batch send to kafka, length: [%s], pending to reprocess msgs', messagesToSend.length); + logger.error(err.stack); + batchMessages = messagesToSend.concat(batchMessages); + batchResolvers = resolvers.concat(batchResolvers); //promises will never be rejected. Will retry forever + } ); - } } From 1d9fc4a322f8067b6e35bc96e8ff28eb13f2ca8e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 1 Jun 2021 18:01:21 +0300 Subject: [PATCH 010/151] js-executor: typo fix --- msa/js-executor/queue/kafkaTemplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 1fac350976..ad243aacdd 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -93,7 +93,7 @@ function sendMessagesAsBatch() { }).then( () => { logger.debug('Response batch sent to kafka, length: [%s]', messagesToSend.length); - for (let i = 0; i < promisesToSend.length; i++) { + for (let i = 0; i < resolvers.length; i++) { resolvers[i](); } }, From 41391dbef8bbfbf1c0f56165dd41277dc45d23fe Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 1 Jun 2021 18:06:10 +0300 Subject: [PATCH 011/151] js-executor: code format --- .../api/jsInvokeMessageProcessor.js | 56 +++++++++---------- msa/js-executor/queue/kafkaTemplate.js | 46 +++++++-------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index 1aabd216d2..bef70936ec 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -43,7 +43,7 @@ function JsInvokeMessageProcessor(producer) { this.lastStatTime = performance.now(); } -JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) { +JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function (message) { var tStart = performance.now(); let requestId; let responseTopic; @@ -78,13 +78,13 @@ JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) { var tFinish = performance.now(); var tTook = tFinish - tStart; - if ( tTook > slowQueryLogMs ) { + if (tTook > slowQueryLogMs) { let functionName; if (request.invokeRequest) { try { buf = Buffer.from(request.invokeRequest['functionName']); functionName = buf.toString('utf8'); - } catch (err){ + } catch (err) { logger.error('[%s] Failed to read functionName from message header: %s', requestId, err.message); logger.error(err.stack); } @@ -97,7 +97,7 @@ JsInvokeMessageProcessor.prototype.onJsInvokeMessage = function(message) { } -JsInvokeMessageProcessor.prototype.processCompileRequest = function(requestId, responseTopic, headers, compileRequest) { +JsInvokeMessageProcessor.prototype.processCompileRequest = function (requestId, responseTopic, headers, compileRequest) { var scriptId = getScriptId(compileRequest); logger.debug('[%s] Processing compile request, scriptId: [%s]', requestId, scriptId); @@ -116,7 +116,7 @@ JsInvokeMessageProcessor.prototype.processCompileRequest = function(requestId, r ); } -JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, responseTopic, headers, invokeRequest) { +JsInvokeMessageProcessor.prototype.processInvokeRequest = function (requestId, responseTopic, headers, invokeRequest) { var scriptId = getScriptId(invokeRequest); logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId); this.executedScriptsCounter++; @@ -160,7 +160,7 @@ JsInvokeMessageProcessor.prototype.processInvokeRequest = function(requestId, re ); } -JsInvokeMessageProcessor.prototype.processReleaseRequest = function(requestId, responseTopic, headers, releaseRequest) { +JsInvokeMessageProcessor.prototype.processReleaseRequest = function (requestId, responseTopic, headers, releaseRequest) { var scriptId = getScriptId(releaseRequest); logger.debug('[%s] Processing release request, scriptId: [%s]', requestId, scriptId); if (this.scriptMap.has(scriptId)) { @@ -193,9 +193,9 @@ JsInvokeMessageProcessor.prototype.sendResponse = function (requestId, responseT ); } -JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scriptBody) { +JsInvokeMessageProcessor.prototype.getOrCompileScript = function (scriptId, scriptBody) { var self = this; - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { if (self.scriptMap.has(scriptId)) { resolve(self.scriptMap.get(scriptId)); } else { @@ -212,7 +212,7 @@ JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scrip }); } -JsInvokeMessageProcessor.prototype.cacheScript = function(scriptId, script) { +JsInvokeMessageProcessor.prototype.cacheScript = function (scriptId, script) { if (!this.scriptMap.has(scriptId)) { this.scriptIds.push(scriptId); while (this.scriptIds.length > maxActiveScripts) { @@ -229,40 +229,40 @@ JsInvokeMessageProcessor.prototype.cacheScript = function(scriptId, script) { function createRemoteResponse(requestId, compileResponse, invokeResponse, releaseResponse) { const requestIdBits = Utils.UUIDToBits(requestId); return { - requestIdMSB: requestIdBits[0], - requestIdLSB: requestIdBits[1], - compileResponse: compileResponse, - invokeResponse: invokeResponse, - releaseResponse: releaseResponse + requestIdMSB: requestIdBits[0], + requestIdLSB: requestIdBits[1], + compileResponse: compileResponse, + invokeResponse: invokeResponse, + releaseResponse: releaseResponse }; } function createCompileResponse(scriptId, success, errorCode, err) { const scriptIdBits = Utils.UUIDToBits(scriptId); - return { - errorCode: errorCode, - success: success, - errorDetails: parseJsErrorDetails(err), - scriptIdMSB: scriptIdBits[0], - scriptIdLSB: scriptIdBits[1] + return { + errorCode: errorCode, + success: success, + errorDetails: parseJsErrorDetails(err), + scriptIdMSB: scriptIdBits[0], + scriptIdLSB: scriptIdBits[1] }; } function createInvokeResponse(result, success, errorCode, err) { - return { - errorCode: errorCode, - success: success, - errorDetails: parseJsErrorDetails(err), - result: result + return { + errorCode: errorCode, + success: success, + errorDetails: parseJsErrorDetails(err), + result: result }; } function createReleaseResponse(scriptId, success) { const scriptIdBits = Utils.UUIDToBits(scriptId); return { - success: success, - scriptIdMSB: scriptIdBits[0], - scriptIdLSB: scriptIdBits[1] + success: success, + scriptIdMSB: scriptIdBits[0], + scriptIdLSB: scriptIdBits[1] }; } diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index ad243aacdd..7fce429d23 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -45,9 +45,9 @@ function KafkaProducer() { const message = { topic: responseTopic, messages: [{ - key: scriptId, - value: rawResponse, - headers: headers.data + key: scriptId, + value: rawResponse, + headers: headers.data }] }; @@ -70,28 +70,28 @@ function pushMessageToSendLater(message) { } function sendLoopWithLinger() { - if (sendLoopInstance) { + if (sendLoopInstance) { logger.debug("Clear sendLoop scheduler. Starting new send loop with linger [%s]", linger); clearInterval(sendLoopInstance); - } else { + } else { logger.debug("Starting new send loop with linger [%s]", linger) - } - sendLoopInstance = setInterval(sendMessagesAsBatch, linger); + } + sendLoopInstance = setInterval(sendMessagesAsBatch, linger); } function sendMessagesAsBatch() { - if (batchMessages.length > 0) { - logger.debug('sendMessagesAsBatch, length: [%s]', batchMessages.length); - const messagesToSend = batchMessages; - const resolvers = batchResolvers; - batchMessages = []; - batchResolvers = []; - producer.sendBatch({ - topicMessages: messagesToSend, - acks: acks, - compression: compressionType - }).then( - () => { + if (batchMessages.length > 0) { + logger.debug('sendMessagesAsBatch, length: [%s]', batchMessages.length); + const messagesToSend = batchMessages; + const resolvers = batchResolvers; + batchMessages = []; + batchResolvers = []; + producer.sendBatch({ + topicMessages: messagesToSend, + acks: acks, + compression: compressionType + }).then( + () => { logger.debug('Response batch sent to kafka, length: [%s]', messagesToSend.length); for (let i = 0; i < resolvers.length; i++) { resolvers[i](); @@ -103,8 +103,8 @@ function sendMessagesAsBatch() { batchMessages = messagesToSend.concat(batchMessages); batchResolvers = resolvers.concat(batchResolvers); //promises will never be rejected. Will retry forever } - ); - } + ); + } } (async () => { @@ -120,8 +120,8 @@ function sendMessagesAsBatch() { let kafkaConfig = { brokers: kafkaBootstrapServers.split(','), - logLevel: logLevel.INFO, - logCreator: KafkaJsWinstonLogCreator + logLevel: logLevel.INFO, + logCreator: KafkaJsWinstonLogCreator }; if (kafkaClientId) { From ab5f1b5b639236fb7528ff9544425e2310c33dbb Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 10:41:24 +0300 Subject: [PATCH 012/151] js-executor: ScriptMap optimize work --- msa/js-executor/api/jsInvokeMessageProcessor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index bef70936ec..aa8f5d2db0 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -196,8 +196,9 @@ JsInvokeMessageProcessor.prototype.sendResponse = function (requestId, responseT JsInvokeMessageProcessor.prototype.getOrCompileScript = function (scriptId, scriptBody) { var self = this; return new Promise(function (resolve, reject) { - if (self.scriptMap.has(scriptId)) { - resolve(self.scriptMap.get(scriptId)); + const script = self.scriptMap.get(scriptId); + if (script !== undefined) { + resolve(script); } else { self.executor.compileScript(scriptBody).then( (script) => { From ff7fa6237f1f342357995c32488f542c984583d9 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 2 Jun 2021 11:05:32 +0300 Subject: [PATCH 013/151] js-executor: zero check and code cleanup --- msa/js-executor/api/jsInvokeMessageProcessor.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.js b/msa/js-executor/api/jsInvokeMessageProcessor.js index aa8f5d2db0..5257a03168 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.js +++ b/msa/js-executor/api/jsInvokeMessageProcessor.js @@ -121,9 +121,9 @@ JsInvokeMessageProcessor.prototype.processInvokeRequest = function (requestId, r logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId); this.executedScriptsCounter++; if (this.executedScriptsCounter % statFrequency == 0) { - var nowMs = performance.now(); - var msSinceLastStat = nowMs - this.lastStatTime; - var requestsPerSec = statFrequency / msSinceLastStat * 1000; //msSinceLastStat can't be zero in the real world + const nowMs = performance.now(); + const msSinceLastStat = nowMs - this.lastStatTime; + const requestsPerSec = msSinceLastStat == 0 ? statFrequency : statFrequency / msSinceLastStat * 1000; this.lastStatTime = nowMs; logger.info('STAT[%s]: requests [%s], took [%s]ms, request/s [%s]', this.executedScriptsCounter, statFrequency, msSinceLastStat, requestsPerSec); } @@ -197,13 +197,13 @@ JsInvokeMessageProcessor.prototype.getOrCompileScript = function (scriptId, scri var self = this; return new Promise(function (resolve, reject) { const script = self.scriptMap.get(scriptId); - if (script !== undefined) { + if (script) { resolve(script); } else { self.executor.compileScript(scriptBody).then( - (script) => { - self.cacheScript(scriptId, script); - resolve(script); + (compiledScript) => { + self.cacheScript(scriptId, compiledScript); + resolve(compiledScript); }, (err) => { reject(err); From 083dc38cf8f587dd10f386095b1ce3cbde826fc6 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 3 Jun 2021 19:32:25 +0300 Subject: [PATCH 014/151] Refactoring of the ttl cleanup services --- .../service/ttl/AbstractCleanUpService.java | 44 ++++--------------- .../{alarms => }/AlarmsCleanUpService.java | 3 +- .../{edge => }/EdgeEventsCleanUpService.java | 34 +++++++------- .../{events => }/EventsCleanUpService.java | 34 +++++++------- ...ice.java => TimeseriesCleanUpService.java} | 25 ++++++----- .../PsqlTimeseriesCleanUpService.java | 44 ------------------- .../TimescaleTimeseriesCleanUpService.java | 36 --------------- .../server/dao/edge/EdgeService.java | 2 + .../server/dao/event/EventService.java | 2 + .../dao/timeseries/TimeseriesService.java | 2 + .../thingsboard/server/dao/edge/EdgeDao.java | 7 +++ .../server/dao/edge/EdgeServiceImpl.java | 5 +++ .../server/dao/event/BaseEventService.java | 5 +++ .../server/dao/event/EventDao.java | 7 +++ ...paAbstractDaoListeningExecutorService.java | 22 ++++++++++ .../server/dao/sql/edge/JpaEdgeDao.java | 18 ++++++++ .../server/dao/sql/event/JpaBaseEventDao.java | 23 +++++++++- .../dao/sqlts/AbstractSqlTimeseriesDao.java | 19 ++++++++ .../dao/sqlts/hsql/JpaHsqlTimeseriesDao.java | 5 +++ .../dao/sqlts/psql/JpaPsqlTimeseriesDao.java | 26 +++++++++++ .../timescale/TimescaleTimeseriesDao.java | 9 ++++ .../dao/timeseries/BaseTimeseriesService.java | 5 +++ .../CassandraBaseTimeseriesDao.java | 5 +++ .../server/dao/timeseries/TimeseriesDao.java | 2 + 24 files changed, 219 insertions(+), 165 deletions(-) rename application/src/main/java/org/thingsboard/server/service/ttl/{alarms => }/AlarmsCleanUpService.java (99%) rename application/src/main/java/org/thingsboard/server/service/ttl/{edge => }/EdgeEventsCleanUpService.java (63%) rename application/src/main/java/org/thingsboard/server/service/ttl/{events => }/EventsCleanUpService.java (63%) rename application/src/main/java/org/thingsboard/server/service/ttl/{timeseries/AbstractTimeseriesCleanUpService.java => TimeseriesCleanUpService.java} (65%) delete mode 100644 application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java index 05799fc643..d0ed543b76 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java @@ -15,8 +15,13 @@ */ package org.thingsboard.server.service.ttl; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.queue.discovery.PartitionService; import java.sql.Connection; import java.sql.DriverManager; @@ -27,43 +32,12 @@ import java.sql.Statement; @Slf4j +@RequiredArgsConstructor public abstract class AbstractCleanUpService { - @Value("${spring.datasource.url}") - protected String dbUrl; + private final PartitionService partitionService; - @Value("${spring.datasource.username}") - protected String dbUserName; - - @Value("${spring.datasource.password}") - protected String dbPassword; - - protected long executeQuery(Connection conn, String query) throws SQLException { - try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(query)) { - if (log.isDebugEnabled()) { - getWarnings(statement); - } - resultSet.next(); - return resultSet.getLong(1); - } - } - - protected void getWarnings(Statement statement) throws SQLException { - SQLWarning warnings = statement.getWarnings(); - if (warnings != null) { - log.debug("{}", warnings.getMessage()); - SQLWarning nextWarning = warnings.getNextWarning(); - while (nextWarning != null) { - log.debug("{}", nextWarning.getMessage()); - nextWarning = nextWarning.getNextWarning(); - } - } + protected boolean isSystemTenantPartitionMine(){ + return partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition(); } - - protected abstract void doCleanUp(Connection connection) throws SQLException; - - protected Connection getConnection() throws SQLException { - return DriverManager.getConnection(dbUrl, dbUserName, dbPassword); - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java similarity index 99% rename from application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java rename to application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java index 3b76a6cbca..051a6c92b2 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ttl.alarms; +package org.thingsboard.server.service.ttl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -52,6 +52,7 @@ import java.util.concurrent.TimeUnit; @Slf4j @RequiredArgsConstructor public class AlarmsCleanUpService { + @Value("${sql.ttl.alarms.removal_batch_size}") private Integer removalBatchSize; diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java rename to application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java index e93a82c7eb..7aa8881bed 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ttl.edge; +package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ttl.AbstractCleanUpService; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -@PsqlDao +@TbCoreComponent @Slf4j @Service public class EdgeEventsCleanUpService extends AbstractCleanUpService { @@ -37,20 +35,20 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.edge_events.enabled}") private boolean ttlTaskExecutionEnabled; + private final EdgeService edgeService; + + public EdgeEventsCleanUpService(PartitionService partitionService, EdgeService edgeService) { + super(partitionService); + this.edgeService = edgeService; + } + @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") public void cleanUp() { - if (ttlTaskExecutionEnabled) { - try (Connection conn = getConnection()) { - doCleanUp(conn); - } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - } + if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { + log.info("Going to cleanup old edge events using ttl: {}s", ttl); + long totalEdgeEventsRemoved = edgeService.cleanupEvents(ttl); + log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); } } - @Override - protected void doCleanUp(Connection connection) throws SQLException { - long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);"); - log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java rename to application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java index 407c88261f..b244e1b8fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ttl.events; +package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ttl.AbstractCleanUpService; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -@PsqlDao +@TbCoreComponent @Slf4j @Service public class EventsCleanUpService extends AbstractCleanUpService { @@ -40,20 +38,20 @@ public class EventsCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.events.enabled}") private boolean ttlTaskExecutionEnabled; + private final EventService eventService; + + public EventsCleanUpService(PartitionService partitionService, EventService eventService) { + super(partitionService); + this.eventService = eventService; + } + @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") public void cleanUp() { - if (ttlTaskExecutionEnabled) { - try (Connection conn = getConnection()) { - doCleanUp(conn); - } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - } + if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { + log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugTtl, ttl); + long totalEventsRemoved = eventService.cleanupEvents(ttl, debugTtl); + log.info("Total events removed by TTL: [{}]", totalEventsRemoved); } } - @Override - protected void doCleanUp(Connection connection) throws SQLException { - long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);"); - log.info("Total events removed by TTL: [{}]", totalEventsRemoved); - } } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java similarity index 65% rename from application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java rename to application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java index ee2d437a22..7094b34e62 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java @@ -13,19 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.ttl.timeseries; +package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.service.ttl.AbstractCleanUpService; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - @Slf4j -public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpService { +public class TimeseriesCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.ts.ts_key_value_ttl}") protected long systemTtl; @@ -33,14 +31,17 @@ public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpSe @Value("${sql.ttl.ts.enabled}") private boolean ttlTaskExecutionEnabled; + private final TimeseriesService timeseriesService; + + public TimeseriesCleanUpService(PartitionService partitionService, TimeseriesService timeseriesService) { + super(partitionService); + this.timeseriesService = timeseriesService; + } + @Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}") public void cleanUp() { - if (ttlTaskExecutionEnabled) { - try (Connection conn = getConnection()) { - doCleanUp(conn); - } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - } + if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { + timeseriesService.cleanup(systemTtl); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java deleted file mode 100644 index 3197f0cbb0..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.ttl.timeseries; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.util.PsqlDao; -import org.thingsboard.server.dao.util.SqlTsDao; - -import java.sql.Connection; -import java.sql.SQLException; - -@SqlTsDao -@PsqlDao -@Service -@Slf4j -public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { - - @Value("${sql.postgres.ts_key_value_partitioning}") - private String partitionType; - - @Override - protected void doCleanUp(Connection connection) throws SQLException { - long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);"); - log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved); - long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);"); - log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); - } -} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java deleted file mode 100644 index 0ed61ef97c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.ttl.timeseries; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.util.TimescaleDBTsDao; - -import java.sql.Connection; -import java.sql.SQLException; - -@TimescaleDBTsDao -@Service -@Slf4j -public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { - - @Override - protected void doCleanUp(Connection connection) throws SQLException { - long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);"); - log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); - } -} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index a7b9145c01..8567077eb1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -93,4 +93,6 @@ public interface EdgeService { Object activateInstance(String licenseSecret, String releaseDate); String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId); + + long cleanupEvents(long ttl); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java index ea25568375..dce49afdb1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -46,4 +46,6 @@ public interface EventService { void removeEvents(TenantId tenantId, EntityId entityId); + long cleanupEvents(long ttl, long debugTtl); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java index 6fcb5ca2ca..b1a2541fc7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java @@ -52,4 +52,6 @@ public interface TimeseriesService { List findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId); List findAllKeysByEntityIds(TenantId tenantId, List entityIds); + + void cleanup(long systemTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index d254c5ee9a..eda1075cd6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -176,4 +176,11 @@ public interface EdgeDao extends Dao { * @return the list of rule chain objects */ ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId); + + /** + * Executes stored procedure to cleanup old edge events. + * @param ttl the ttl for edge events in seconds + * @return the number of deleted edge events + */ + long cleanupEvents(long ttl); } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 2ff7d94f43..d06ce8d722 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -627,6 +627,11 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic return result.toString(); } + @Override + public long cleanupEvents(long ttl) { + return edgeDao.cleanupEvents(ttl); + } + private List findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { List result = new ArrayList<>(); PageLink pageLink = new PageLink(DEFAULT_LIMIT); diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index 91bfa954fd..4cfd0a3ffa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -131,6 +131,11 @@ public class BaseEventService implements EventService { } while (eventPageData.hasNext()); } + @Override + public long cleanupEvents(long ttl, long debugTtl) { + return eventDao.cleanupEvents(ttl, debugTtl); + } + private DataValidator eventValidator = new DataValidator() { @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java index ba4e86c95e..d943937ca3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java @@ -102,4 +102,11 @@ public interface EventDao extends Dao { */ List findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit); + /** + * Executes stored procedure to cleanup old events. Uses separate ttl for debug and other events. + * @param otherEventsTtl the ttl for events in seconds + * @param debugEventsTtl the ttl for debug events in seconds + * @return the number of deleted events + */ + long cleanupEvents(long otherEventsTtl, long debugEventsTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java index 4431356690..fcd679383f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java @@ -15,11 +15,33 @@ */ package org.thingsboard.server.dao.sql; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import javax.sql.DataSource; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; + +@Slf4j public abstract class JpaAbstractDaoListeningExecutorService { @Autowired protected JpaExecutorService service; + @Autowired + protected DataSource dataSource; + + protected void printWarnings(Statement statement) throws SQLException { + SQLWarning warnings = statement.getWarnings(); + if (warnings != null) { + log.debug("{}", warnings.getMessage()); + SQLWarning nextWarning = warnings.getNextWarning(); + while (nextWarning != null) { + log.debug("{}", nextWarning.getMessage()); + nextWarning = nextWarning.getNextWarning(); + } + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index f17196fe93..8e7112e81d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -40,6 +40,9 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -194,6 +197,21 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return transformFromRelationToEdge(tenantId, relations); } + @Override + public long cleanupEvents(long ttl) { + try { + CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_edge_events_by_ttl(?,?,?)}"); + stmt.setLong(1, ttl); + stmt.registerOutParameter(3, Types.BIGINT); + stmt.executeUpdate(); + printWarnings(stmt); + return stmt.getLong(3); + } catch (SQLException e) { + log.error("SQLException occurred during TTL task execution ", e); + return 0; + } + } + private ListenableFuture> transformFromRelationToEdge(UUID tenantId, ListenableFuture> relations) { return Futures.transformAsync(relations, input -> { List> edgeFutures = new ArrayList<>(input.size()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java index 44848ec515..edaf95a29d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java @@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.event.DebugEvent; import org.thingsboard.server.common.data.event.ErrorEventFilter; import org.thingsboard.server.common.data.event.EventFilter; -import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.event.LifeCycleEventFilter; import org.thingsboard.server.common.data.event.StatisticsEventFilter; import org.thingsboard.server.common.data.id.EntityId; @@ -40,6 +39,12 @@ import org.thingsboard.server.dao.event.EventDao; import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; +import javax.sql.DataSource; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -256,6 +261,22 @@ public class JpaBaseEventDao extends JpaAbstractDao implemen return DaoUtil.convertDataList(latest); } + @Override + public long cleanupEvents(long otherEventsTtl, long debugEventsTtl) { + try { + CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_events_by_ttl(?,?,?)}"); + stmt.setLong(1, otherEventsTtl); + stmt.setLong(2, debugEventsTtl); + stmt.registerOutParameter(3, Types.BIGINT); + stmt.executeUpdate(); + printWarnings(stmt); + return stmt.getLong(3); + } catch (SQLException e) { + log.error("SQLException occurred during TTL task execution ", e); + return 0; + } + } + public Optional save(EventEntity entity, boolean ifNotExists) { log.debug("Save event [{}] ", entity); if (entity.getTenantId() == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index f6a6b56be5..abd8f15e77 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -25,9 +25,13 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import javax.annotation.Nullable; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -62,6 +66,21 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries @Value("${sql.ttl.ts.ts_key_value_ttl:0}") private long systemTtl; + public void cleanup(long systemTtl) { + try { + log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); + CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_timeseries_by_ttl(?,?,?)}"); + stmt.setObject(1, ModelConstants.NULL_UUID); + stmt.setLong(2, systemTtl); + stmt.registerOutParameter(3, Types.BIGINT); + stmt.executeUpdate(); + printWarnings(stmt); + log.info("Total telemetry removed stats by TTL for entities: [{}]", stmt.getLong(3)); + } catch (SQLException e) { + log.error("SQLException occurred during TTL task execution ", e); + } + } + protected ListenableFuture> processFindAllAsync(TenantId tenantId, EntityId entityId, List queries) { List>> futures = queries .stream() diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java index c01d91ec61..c8241714f4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/JpaHsqlTimeseriesDao.java @@ -54,4 +54,9 @@ public class JpaHsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa return Futures.transform(tsQueue.add(entity), v -> dataPointDays, MoreExecutors.directExecutor()); } + @Override + public void cleanup(long systemTtl) { + + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java index 64c074fd40..594f77ae78 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java @@ -27,6 +27,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository; @@ -35,6 +36,9 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -62,6 +66,7 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa @Value("${sql.postgres.ts_key_value_partitioning:MONTHS}") private String partitioning; + @Override protected void init() { super.init(); @@ -93,6 +98,27 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa return Futures.transform(tsQueue.add(entity), v -> dataPointDays, MoreExecutors.directExecutor()); } + @Override + public void cleanup(long systemTtl) { + cleanupPartitions(systemTtl); + super.cleanup(systemTtl); + } + + private void cleanupPartitions(long systemTtl) { + try { + log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); + CallableStatement stmt = dataSource.getConnection().prepareCall("{call drop_partitions_by_max_ttl(?,?,?)}"); + stmt.setObject(1, partitioning); + stmt.setLong(2, systemTtl); + stmt.registerOutParameter(3, Types.BIGINT); + stmt.executeUpdate(); + printWarnings(stmt); + log.info("Total partitions removed by TTL: [{}]", stmt.getLong(3)); + } catch (SQLException e) { + log.error("SQLException occurred during TTL task execution ", e); + } + } + private void savePartitionIfNotExist(long ts) { if (!tsFormat.equals(SqlTsPartitionDate.INDEFINITE) && ts >= 0) { LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts), ZoneOffset.UTC); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java index 7f798ddb4b..31f3407fbf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; @@ -45,6 +46,9 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.sql.CallableStatement; +import java.sql.SQLException; +import java.sql.Types; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -156,6 +160,11 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements } } + @Override + public void cleanup(long systemTtl) { + super.cleanup(systemTtl); + } + private ListenableFuture> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) { String strKey = query.getKey(); Integer keyId = getOrSaveKeyId(strKey); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index 701c67a648..fb15af723a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -126,6 +126,11 @@ public class BaseTimeseriesService implements TimeseriesService { return timeseriesLatestDao.findAllKeysByEntityIds(tenantId, entityIds); } + @Override + public void cleanup(long systemTtl) { + timeseriesDao.cleanup(systemTtl); + } + @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) { validate(entityId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 240d5a0b88..ce653e2e6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -288,6 +288,11 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD } } + @Override + public void cleanup(long systemTtl) { + //Cleanup by TTL is native for Cassandra + } + private ListenableFuture> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) { long minPartition = toPartitionTs(query.getStartTs()); long maxPartition = toPartitionTs(query.getEndTs()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java index 3b3eb4ee0a..e9af5f0b75 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesDao.java @@ -38,4 +38,6 @@ public interface TimeseriesDao { ListenableFuture remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query); ListenableFuture removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query); + + void cleanup(long systemTtl); } From d1292a0d04596c3453514b2d973b38a469221ecc Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 7 Jun 2021 13:23:01 +0300 Subject: [PATCH 015/151] refactoring ttl cleanup services --- .../schema_update_psql_drop_partitions.sql | 174 +++++++++++++----- .../service/ttl/EdgeEventsCleanUpService.java | 4 +- .../service/ttl/EventsCleanUpService.java | 4 +- .../service/ttl/TimeseriesCleanUpService.java | 4 + .../server/dao/edge/EdgeService.java | 2 +- .../server/dao/event/EventService.java | 2 +- .../thingsboard/server/dao/edge/EdgeDao.java | 3 +- .../server/dao/edge/EdgeServiceImpl.java | 4 +- .../server/dao/event/BaseEventService.java | 4 +- .../server/dao/event/EventDao.java | 3 +- .../server/dao/sql/edge/JpaEdgeDao.java | 20 +- .../server/dao/sql/event/JpaBaseEventDao.java | 23 ++- .../dao/sqlts/AbstractSqlTimeseriesDao.java | 16 +- .../dao/sqlts/psql/JpaPsqlTimeseriesDao.java | 17 +- dao/src/main/resources/sql/schema-ts-psql.sql | 174 +++++++++++++----- 15 files changed, 312 insertions(+), 142 deletions(-) diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql index fcc5c6f232..9d336e0330 100644 --- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql +++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar LANGUAGE plpgsql AS $$ DECLARE - max_tenant_ttl bigint; - max_customer_ttl bigint; - max_ttl bigint; - date timestamp; - partition_by_max_ttl_date varchar; - partition_month varchar; - partition_day varchar; - partition_year varchar; - partition varchar; - partition_to_delete varchar; - + max_tenant_ttl bigint; + max_customer_ttl bigint; + max_ttl bigint; + date timestamp; + partition_by_max_ttl_date varchar; + partition_by_max_ttl_month varchar; + partition_by_max_ttl_day varchar; + partition_by_max_ttl_year varchar; + partition varchar; + partition_year integer; + partition_month integer; + partition_day integer; BEGIN SELECT max(attribute_kv.long_v) @@ -45,53 +46,138 @@ BEGIN if max_ttl IS NOT NULL AND max_ttl > 0 THEN date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); + RAISE NOTICE 'Date by max ttl: %', date; RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; IF partition_by_max_ttl_date IS NOT NULL THEN CASE WHEN partition_type = 'DAYS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); WHEN partition_type = 'MONTHS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); ELSE - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); END CASE; - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - LOOP - IF partition != partition_by_max_ttl_date THEN - IF partition_year IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN - partition_to_delete := partition; - ELSE - IF partition_month IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN - partition_to_delete := partition; + IF partition_by_max_ttl_year IS NULL THEN + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; + ELSE + IF partition_type = 'YEARS' THEN + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END LOOP; + ELSE + IF partition_type = 'MONTHS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; ELSE - IF partition_day IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN - partition_to_delete := partition; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_year = partition_by_max_ttl_year::integer THEN + IF partition_month >= partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; END IF; END IF; END IF; + END LOOP; + END IF; + ELSE + IF partition_type = 'DAYS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + IF partition_by_max_ttl_day IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_month > partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_month < partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_day := SPLIT_PART(partition, '_', 5)::integer; + IF partition_day >= partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_day < partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END LOOP; END IF; END IF; END IF; - IF partition_to_delete IS NOT NULL THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete); - partition_to_delete := NULL; - deleted := deleted + 1; - END IF; END IF; - END LOOP; + END IF; + END IF; END IF; END IF; END @@ -107,8 +193,6 @@ BEGIN partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); WHEN partition_type = 'YEARS' THEN partition := 'ts_kv_' || to_char(date, 'yyyy'); - WHEN partition_type = 'INDEFINITE' THEN - partition := NULL; ELSE partition := NULL; END CASE; diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java index 7aa8881bed..3cdd1f71bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java @@ -45,9 +45,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { - log.info("Going to cleanup old edge events using ttl: {}s", ttl); - long totalEdgeEventsRemoved = edgeService.cleanupEvents(ttl); - log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); + edgeService.cleanupEvents(ttl); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java index b244e1b8fd..a51910b7ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java @@ -48,9 +48,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { - log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugTtl, ttl); - long totalEventsRemoved = eventService.cleanupEvents(ttl, debugTtl); - log.info("Total events removed by TTL: [{}]", totalEventsRemoved); + eventService.cleanupEvents(ttl, debugTtl); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java index 7094b34e62..55c746b580 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java @@ -18,11 +18,15 @@ package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ttl.AbstractCleanUpService; +@TbCoreComponent @Slf4j +@Service public class TimeseriesCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.ts.ts_key_value_ttl}") diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 8567077eb1..53472fd261 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -94,5 +94,5 @@ public interface EdgeService { String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId); - long cleanupEvents(long ttl); + void cleanupEvents(long ttl); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java index dce49afdb1..db1c77697e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -46,6 +46,6 @@ public interface EventService { void removeEvents(TenantId tenantId, EntityId entityId); - long cleanupEvents(long ttl, long debugTtl); + void cleanupEvents(long ttl, long debugTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index eda1075cd6..60ef98a801 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -180,7 +180,6 @@ public interface EdgeDao extends Dao { /** * Executes stored procedure to cleanup old edge events. * @param ttl the ttl for edge events in seconds - * @return the number of deleted edge events */ - long cleanupEvents(long ttl); + void cleanupEvents(long ttl); } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index d06ce8d722..0089853969 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -628,8 +628,8 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } @Override - public long cleanupEvents(long ttl) { - return edgeDao.cleanupEvents(ttl); + public void cleanupEvents(long ttl) { + edgeDao.cleanupEvents(ttl); } private List findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index 4cfd0a3ffa..2785df90df 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -132,8 +132,8 @@ public class BaseEventService implements EventService { } @Override - public long cleanupEvents(long ttl, long debugTtl) { - return eventDao.cleanupEvents(ttl, debugTtl); + public void cleanupEvents(long ttl, long debugTtl) { + eventDao.cleanupEvents(ttl, debugTtl); } private DataValidator eventValidator = diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java index d943937ca3..ceacbffd50 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java @@ -106,7 +106,6 @@ public interface EventDao extends Dao { * Executes stored procedure to cleanup old events. Uses separate ttl for debug and other events. * @param otherEventsTtl the ttl for events in seconds * @param debugEventsTtl the ttl for debug events in seconds - * @return the number of deleted events */ - long cleanupEvents(long otherEventsTtl, long debugEventsTtl); + void cleanupEvents(long otherEventsTtl, long debugEventsTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 8e7112e81d..ec6d730230 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -40,9 +40,9 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -198,17 +198,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple } @Override - public long cleanupEvents(long ttl) { + public void cleanupEvents(long ttl) { try { - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_edge_events_by_ttl(?,?,?)}"); + log.info("Going to cleanup old edge events using ttl: {}s", ttl); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_edge_events_by_ttl(?,?)"); stmt.setLong(1, ttl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(2, 0); + stmt.execute(); printWarnings(stmt); - return stmt.getLong(3); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - return 0; + log.error("SQLException occurred during edge events TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java index edaf95a29d..cde7d75d32 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java @@ -39,12 +39,9 @@ import org.thingsboard.server.dao.event.EventDao; import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import javax.sql.DataSource; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Statement; -import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -262,18 +259,20 @@ public class JpaBaseEventDao extends JpaAbstractDao implemen } @Override - public long cleanupEvents(long otherEventsTtl, long debugEventsTtl) { + public void cleanupEvents(long otherEventsTtl, long debugEventsTtl) { try { - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_events_by_ttl(?,?,?)}"); + log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugEventsTtl, otherEventsTtl); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_events_by_ttl(?,?,?)"); stmt.setLong(1, otherEventsTtl); stmt.setLong(2, debugEventsTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - return stmt.getLong(3); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total events removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - return 0; + log.error("SQLException occurred during events TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index abd8f15e77..f1636740c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -29,9 +29,9 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import javax.annotation.Nullable; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -69,15 +69,17 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries public void cleanup(long systemTtl) { try { log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_timeseries_by_ttl(?,?,?)}"); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_timeseries_by_ttl(?,?,?)"); stmt.setObject(1, ModelConstants.NULL_UUID); stmt.setLong(2, systemTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - log.info("Total telemetry removed stats by TTL for entities: [{}]", stmt.getLong(3)); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total telemetry removed stats by TTL for entities: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); + log.error("SQLException occurred during timeseries TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java index 594f77ae78..9b18dcde7e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java @@ -27,7 +27,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository; @@ -36,9 +35,9 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -107,13 +106,15 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa private void cleanupPartitions(long systemTtl) { try { log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); - CallableStatement stmt = dataSource.getConnection().prepareCall("{call drop_partitions_by_max_ttl(?,?,?)}"); - stmt.setObject(1, partitioning); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call drop_partitions_by_max_ttl(?,?,?)"); + stmt.setString(1, partitioning); stmt.setLong(2, systemTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - log.info("Total partitions removed by TTL: [{}]", stmt.getLong(3)); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total partitions removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); } diff --git a/dao/src/main/resources/sql/schema-ts-psql.sql b/dao/src/main/resources/sql/schema-ts-psql.sql index 5683cc0a17..2744ff5a07 100644 --- a/dao/src/main/resources/sql/schema-ts-psql.sql +++ b/dao/src/main/resources/sql/schema-ts-psql.sql @@ -38,17 +38,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar LANGUAGE plpgsql AS $$ DECLARE - max_tenant_ttl bigint; - max_customer_ttl bigint; - max_ttl bigint; - date timestamp; - partition_by_max_ttl_date varchar; - partition_month varchar; - partition_day varchar; - partition_year varchar; - partition varchar; - partition_to_delete varchar; - + max_tenant_ttl bigint; + max_customer_ttl bigint; + max_ttl bigint; + date timestamp; + partition_by_max_ttl_date varchar; + partition_by_max_ttl_month varchar; + partition_by_max_ttl_day varchar; + partition_by_max_ttl_year varchar; + partition varchar; + partition_year integer; + partition_month integer; + partition_day integer; BEGIN SELECT max(attribute_kv.long_v) @@ -65,53 +66,138 @@ BEGIN if max_ttl IS NOT NULL AND max_ttl > 0 THEN date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); + RAISE NOTICE 'Date by max ttl: %', date; RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; IF partition_by_max_ttl_date IS NOT NULL THEN CASE WHEN partition_type = 'DAYS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); WHEN partition_type = 'MONTHS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); ELSE - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); END CASE; - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - LOOP - IF partition != partition_by_max_ttl_date THEN - IF partition_year IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN - partition_to_delete := partition; - ELSE - IF partition_month IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN - partition_to_delete := partition; + IF partition_by_max_ttl_year IS NULL THEN + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; + ELSE + IF partition_type = 'YEARS' THEN + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END LOOP; + ELSE + IF partition_type = 'MONTHS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; ELSE - IF partition_day IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN - partition_to_delete := partition; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_year = partition_by_max_ttl_year::integer THEN + IF partition_month >= partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; END IF; END IF; END IF; + END LOOP; + END IF; + ELSE + IF partition_type = 'DAYS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + IF partition_by_max_ttl_day IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_month > partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_month < partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_day := SPLIT_PART(partition, '_', 5)::integer; + IF partition_day >= partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_day < partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END LOOP; END IF; END IF; END IF; - IF partition_to_delete IS NOT NULL THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete); - partition_to_delete := NULL; - deleted := deleted + 1; - END IF; END IF; - END LOOP; + END IF; + END IF; END IF; END IF; END @@ -127,8 +213,6 @@ BEGIN partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); WHEN partition_type = 'YEARS' THEN partition := 'ts_kv_' || to_char(date, 'yyyy'); - WHEN partition_type = 'INDEFINITE' THEN - partition := NULL; ELSE partition := NULL; END CASE; From 6af34cf043da68943fd4aa26c608e548c3140776 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 7 Jun 2021 13:23:01 +0300 Subject: [PATCH 016/151] refactoring ttl cleanup services --- .../schema_update_psql_drop_partitions.sql | 174 +++++++++++++----- .../service/ttl/EdgeEventsCleanUpService.java | 4 +- .../service/ttl/EventsCleanUpService.java | 4 +- .../service/ttl/TimeseriesCleanUpService.java | 4 + .../server/dao/edge/EdgeService.java | 2 +- .../server/dao/event/EventService.java | 2 +- .../thingsboard/server/dao/edge/EdgeDao.java | 3 +- .../server/dao/edge/EdgeServiceImpl.java | 4 +- .../server/dao/event/BaseEventService.java | 4 +- .../server/dao/event/EventDao.java | 3 +- .../server/dao/sql/edge/JpaEdgeDao.java | 20 +- .../server/dao/sql/event/JpaBaseEventDao.java | 23 ++- .../dao/sqlts/AbstractSqlTimeseriesDao.java | 16 +- .../dao/sqlts/psql/JpaPsqlTimeseriesDao.java | 17 +- dao/src/main/resources/sql/schema-ts-psql.sql | 174 +++++++++++++----- 15 files changed, 312 insertions(+), 142 deletions(-) diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql index fcc5c6f232..9d336e0330 100644 --- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql +++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar LANGUAGE plpgsql AS $$ DECLARE - max_tenant_ttl bigint; - max_customer_ttl bigint; - max_ttl bigint; - date timestamp; - partition_by_max_ttl_date varchar; - partition_month varchar; - partition_day varchar; - partition_year varchar; - partition varchar; - partition_to_delete varchar; - + max_tenant_ttl bigint; + max_customer_ttl bigint; + max_ttl bigint; + date timestamp; + partition_by_max_ttl_date varchar; + partition_by_max_ttl_month varchar; + partition_by_max_ttl_day varchar; + partition_by_max_ttl_year varchar; + partition varchar; + partition_year integer; + partition_month integer; + partition_day integer; BEGIN SELECT max(attribute_kv.long_v) @@ -45,53 +46,138 @@ BEGIN if max_ttl IS NOT NULL AND max_ttl > 0 THEN date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); + RAISE NOTICE 'Date by max ttl: %', date; RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; IF partition_by_max_ttl_date IS NOT NULL THEN CASE WHEN partition_type = 'DAYS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); WHEN partition_type = 'MONTHS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); ELSE - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); END CASE; - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - LOOP - IF partition != partition_by_max_ttl_date THEN - IF partition_year IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN - partition_to_delete := partition; - ELSE - IF partition_month IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN - partition_to_delete := partition; + IF partition_by_max_ttl_year IS NULL THEN + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; + ELSE + IF partition_type = 'YEARS' THEN + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END LOOP; + ELSE + IF partition_type = 'MONTHS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; ELSE - IF partition_day IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN - partition_to_delete := partition; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_year = partition_by_max_ttl_year::integer THEN + IF partition_month >= partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; END IF; END IF; END IF; + END LOOP; + END IF; + ELSE + IF partition_type = 'DAYS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + IF partition_by_max_ttl_day IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_month > partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_month < partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_day := SPLIT_PART(partition, '_', 5)::integer; + IF partition_day >= partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_day < partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END LOOP; END IF; END IF; END IF; - IF partition_to_delete IS NOT NULL THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete); - partition_to_delete := NULL; - deleted := deleted + 1; - END IF; END IF; - END LOOP; + END IF; + END IF; END IF; END IF; END @@ -107,8 +193,6 @@ BEGIN partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); WHEN partition_type = 'YEARS' THEN partition := 'ts_kv_' || to_char(date, 'yyyy'); - WHEN partition_type = 'INDEFINITE' THEN - partition := NULL; ELSE partition := NULL; END CASE; diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java index 7aa8881bed..3cdd1f71bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EdgeEventsCleanUpService.java @@ -45,9 +45,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { - log.info("Going to cleanup old edge events using ttl: {}s", ttl); - long totalEdgeEventsRemoved = edgeService.cleanupEvents(ttl); - log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); + edgeService.cleanupEvents(ttl); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java index b244e1b8fd..a51910b7ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/EventsCleanUpService.java @@ -48,9 +48,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { - log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugTtl, ttl); - long totalEventsRemoved = eventService.cleanupEvents(ttl, debugTtl); - log.info("Total events removed by TTL: [{}]", totalEventsRemoved); + eventService.cleanupEvents(ttl, debugTtl); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java index 7094b34e62..55c746b580 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/TimeseriesCleanUpService.java @@ -18,11 +18,15 @@ package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ttl.AbstractCleanUpService; +@TbCoreComponent @Slf4j +@Service public class TimeseriesCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.ts.ts_key_value_ttl}") diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 8567077eb1..53472fd261 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -94,5 +94,5 @@ public interface EdgeService { String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId); - long cleanupEvents(long ttl); + void cleanupEvents(long ttl); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java index dce49afdb1..db1c77697e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -46,6 +46,6 @@ public interface EventService { void removeEvents(TenantId tenantId, EntityId entityId); - long cleanupEvents(long ttl, long debugTtl); + void cleanupEvents(long ttl, long debugTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index eda1075cd6..60ef98a801 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -180,7 +180,6 @@ public interface EdgeDao extends Dao { /** * Executes stored procedure to cleanup old edge events. * @param ttl the ttl for edge events in seconds - * @return the number of deleted edge events */ - long cleanupEvents(long ttl); + void cleanupEvents(long ttl); } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index d06ce8d722..0089853969 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -628,8 +628,8 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic } @Override - public long cleanupEvents(long ttl) { - return edgeDao.cleanupEvents(ttl); + public void cleanupEvents(long ttl) { + edgeDao.cleanupEvents(ttl); } private List findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index 4cfd0a3ffa..2785df90df 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -132,8 +132,8 @@ public class BaseEventService implements EventService { } @Override - public long cleanupEvents(long ttl, long debugTtl) { - return eventDao.cleanupEvents(ttl, debugTtl); + public void cleanupEvents(long ttl, long debugTtl) { + eventDao.cleanupEvents(ttl, debugTtl); } private DataValidator eventValidator = diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java index d943937ca3..ceacbffd50 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java @@ -106,7 +106,6 @@ public interface EventDao extends Dao { * Executes stored procedure to cleanup old events. Uses separate ttl for debug and other events. * @param otherEventsTtl the ttl for events in seconds * @param debugEventsTtl the ttl for debug events in seconds - * @return the number of deleted events */ - long cleanupEvents(long otherEventsTtl, long debugEventsTtl); + void cleanupEvents(long otherEventsTtl, long debugEventsTtl); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 8e7112e81d..ec6d730230 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -40,9 +40,9 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -198,17 +198,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple } @Override - public long cleanupEvents(long ttl) { + public void cleanupEvents(long ttl) { try { - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_edge_events_by_ttl(?,?,?)}"); + log.info("Going to cleanup old edge events using ttl: {}s", ttl); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_edge_events_by_ttl(?,?)"); stmt.setLong(1, ttl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(2, 0); + stmt.execute(); printWarnings(stmt); - return stmt.getLong(3); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - return 0; + log.error("SQLException occurred during edge events TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java index edaf95a29d..cde7d75d32 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java @@ -39,12 +39,9 @@ import org.thingsboard.server.dao.event.EventDao; import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import javax.sql.DataSource; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.Statement; -import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -262,18 +259,20 @@ public class JpaBaseEventDao extends JpaAbstractDao implemen } @Override - public long cleanupEvents(long otherEventsTtl, long debugEventsTtl) { + public void cleanupEvents(long otherEventsTtl, long debugEventsTtl) { try { - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_events_by_ttl(?,?,?)}"); + log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugEventsTtl, otherEventsTtl); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_events_by_ttl(?,?,?)"); stmt.setLong(1, otherEventsTtl); stmt.setLong(2, debugEventsTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - return stmt.getLong(3); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total events removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); - return 0; + log.error("SQLException occurred during events TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index abd8f15e77..f1636740c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -29,9 +29,9 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import javax.annotation.Nullable; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -69,15 +69,17 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries public void cleanup(long systemTtl) { try { log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_timeseries_by_ttl(?,?,?)}"); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_timeseries_by_ttl(?,?,?)"); stmt.setObject(1, ModelConstants.NULL_UUID); stmt.setLong(2, systemTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - log.info("Total telemetry removed stats by TTL for entities: [{}]", stmt.getLong(3)); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total telemetry removed stats by TTL for entities: [{}]", resultSet.getLong(1)); } catch (SQLException e) { - log.error("SQLException occurred during TTL task execution ", e); + log.error("SQLException occurred during timeseries TTL task execution ", e); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java index 594f77ae78..9b18dcde7e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java @@ -27,7 +27,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository; @@ -36,9 +35,9 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; -import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -107,13 +106,15 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa private void cleanupPartitions(long systemTtl) { try { log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); - CallableStatement stmt = dataSource.getConnection().prepareCall("{call drop_partitions_by_max_ttl(?,?,?)}"); - stmt.setObject(1, partitioning); + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call drop_partitions_by_max_ttl(?,?,?)"); + stmt.setString(1, partitioning); stmt.setLong(2, systemTtl); - stmt.registerOutParameter(3, Types.BIGINT); - stmt.executeUpdate(); + stmt.setLong(3, 0); + stmt.execute(); printWarnings(stmt); - log.info("Total partitions removed by TTL: [{}]", stmt.getLong(3)); + ResultSet resultSet = stmt.getResultSet(); + resultSet.next(); + log.info("Total partitions removed by TTL: [{}]", resultSet.getLong(1)); } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); } diff --git a/dao/src/main/resources/sql/schema-ts-psql.sql b/dao/src/main/resources/sql/schema-ts-psql.sql index 5683cc0a17..2744ff5a07 100644 --- a/dao/src/main/resources/sql/schema-ts-psql.sql +++ b/dao/src/main/resources/sql/schema-ts-psql.sql @@ -38,17 +38,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar LANGUAGE plpgsql AS $$ DECLARE - max_tenant_ttl bigint; - max_customer_ttl bigint; - max_ttl bigint; - date timestamp; - partition_by_max_ttl_date varchar; - partition_month varchar; - partition_day varchar; - partition_year varchar; - partition varchar; - partition_to_delete varchar; - + max_tenant_ttl bigint; + max_customer_ttl bigint; + max_ttl bigint; + date timestamp; + partition_by_max_ttl_date varchar; + partition_by_max_ttl_month varchar; + partition_by_max_ttl_day varchar; + partition_by_max_ttl_year varchar; + partition varchar; + partition_year integer; + partition_month integer; + partition_day integer; BEGIN SELECT max(attribute_kv.long_v) @@ -65,53 +66,138 @@ BEGIN if max_ttl IS NOT NULL AND max_ttl > 0 THEN date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); + RAISE NOTICE 'Date by max ttl: %', date; RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; IF partition_by_max_ttl_date IS NOT NULL THEN CASE WHEN partition_type = 'DAYS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); WHEN partition_type = 'MONTHS' THEN - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); ELSE - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); END CASE; - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - LOOP - IF partition != partition_by_max_ttl_date THEN - IF partition_year IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN - partition_to_delete := partition; - ELSE - IF partition_month IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN - partition_to_delete := partition; + IF partition_by_max_ttl_year IS NULL THEN + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; + ELSE + IF partition_type = 'YEARS' THEN + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END LOOP; + ELSE + IF partition_type = 'MONTHS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; ELSE - IF partition_day IS NOT NULL THEN - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN - partition_to_delete := partition; + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_year = partition_by_max_ttl_year::integer THEN + IF partition_month >= partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; END IF; END IF; END IF; + END LOOP; + END IF; + ELSE + IF partition_type = 'DAYS' THEN + IF partition_by_max_ttl_month IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; + ELSE + IF partition_by_max_ttl_day IS NULL THEN + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; + ELSE + FOR partition IN SELECT tablename + FROM pg_tables + WHERE schemaname = 'public' + AND tablename like 'ts_kv_' || '%' + AND tablename != 'ts_kv_latest' + AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' + AND tablename != partition_by_max_ttl_date + LOOP + partition_year := SPLIT_PART(partition, '_', 3)::integer; + IF partition_year > partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_year < partition_by_max_ttl_year::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_month := SPLIT_PART(partition, '_', 4)::integer; + IF partition_month > partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_month < partition_by_max_ttl_month::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + ELSE + partition_day := SPLIT_PART(partition, '_', 5)::integer; + IF partition_day >= partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; + CONTINUE; + ELSE + IF partition_day < partition_by_max_ttl_day::integer THEN + RAISE NOTICE 'Partition to delete by max ttl: %', partition; + EXECUTE format('DROP TABLE IF EXISTS %I', partition); + deleted := deleted + 1; + END IF; + END IF; + END IF; + END IF; + END IF; + END IF; + END LOOP; END IF; END IF; END IF; - IF partition_to_delete IS NOT NULL THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete); - partition_to_delete := NULL; - deleted := deleted + 1; - END IF; END IF; - END LOOP; + END IF; + END IF; END IF; END IF; END @@ -127,8 +213,6 @@ BEGIN partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); WHEN partition_type = 'YEARS' THEN partition := 'ts_kv_' || to_char(date, 'yyyy'); - WHEN partition_type = 'INDEFINITE' THEN - partition := NULL; ELSE partition := NULL; END CASE; From 9c633eeebe04f4293943c0d7d9198a09960846ec Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Mon, 7 Jun 2021 15:59:30 +0300 Subject: [PATCH 017/151] added try with resource to auto-close the connections, statements and result sets --- .../server/dao/sql/edge/JpaEdgeDao.java | 14 ++++++++------ .../server/dao/sql/event/JpaBaseEventDao.java | 14 ++++++++------ .../server/dao/sqlts/AbstractSqlTimeseriesDao.java | 14 ++++++++------ .../dao/sqlts/psql/JpaPsqlTimeseriesDao.java | 14 ++++++++------ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index ec6d730230..2249de109f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -40,6 +40,7 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -199,16 +200,17 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple @Override public void cleanupEvents(long ttl) { - try { - log.info("Going to cleanup old edge events using ttl: {}s", ttl); - PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_edge_events_by_ttl(?,?)"); + log.info("Going to cleanup old edge events using ttl: {}s", ttl); + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement("call cleanup_edge_events_by_ttl(?,?)")) { stmt.setLong(1, ttl); stmt.setLong(2, 0); stmt.execute(); printWarnings(stmt); - ResultSet resultSet = stmt.getResultSet(); - resultSet.next(); - log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1)); + try (ResultSet resultSet = stmt.getResultSet()) { + resultSet.next(); + log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1)); + } } catch (SQLException e) { log.error("SQLException occurred during edge events TTL task execution ", e); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java index cde7d75d32..ee09bb7a90 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java @@ -39,6 +39,7 @@ import org.thingsboard.server.dao.event.EventDao; import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -260,17 +261,18 @@ public class JpaBaseEventDao extends JpaAbstractDao implemen @Override public void cleanupEvents(long otherEventsTtl, long debugEventsTtl) { - try { - log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugEventsTtl, otherEventsTtl); - PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_events_by_ttl(?,?,?)"); + log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugEventsTtl, otherEventsTtl); + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement("call cleanup_events_by_ttl(?,?,?)")) { stmt.setLong(1, otherEventsTtl); stmt.setLong(2, debugEventsTtl); stmt.setLong(3, 0); stmt.execute(); printWarnings(stmt); - ResultSet resultSet = stmt.getResultSet(); - resultSet.next(); - log.info("Total events removed by TTL: [{}]", resultSet.getLong(1)); + try (ResultSet resultSet = stmt.getResultSet()){ + resultSet.next(); + log.info("Total events removed by TTL: [{}]", resultSet.getLong(1)); + } } catch (SQLException e) { log.error("SQLException occurred during events TTL task execution ", e); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index f1636740c3..eb12984753 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import javax.annotation.Nullable; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -67,17 +68,18 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries private long systemTtl; public void cleanup(long systemTtl) { - try { - log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); - PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_timeseries_by_ttl(?,?,?)"); + log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement("call cleanup_timeseries_by_ttl(?,?,?)")) { stmt.setObject(1, ModelConstants.NULL_UUID); stmt.setLong(2, systemTtl); stmt.setLong(3, 0); stmt.execute(); printWarnings(stmt); - ResultSet resultSet = stmt.getResultSet(); - resultSet.next(); - log.info("Total telemetry removed stats by TTL for entities: [{}]", resultSet.getLong(1)); + try (ResultSet resultSet = stmt.getResultSet()) { + resultSet.next(); + log.info("Total telemetry removed stats by TTL for entities: [{}]", resultSet.getLong(1)); + } } catch (SQLException e) { log.error("SQLException occurred during timeseries TTL task execution ", e); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java index 9b18dcde7e..c23e615548 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlTsDao; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -104,17 +105,18 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa } private void cleanupPartitions(long systemTtl) { - try { - log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); - PreparedStatement stmt = dataSource.getConnection().prepareStatement("call drop_partitions_by_max_ttl(?,?,?)"); + log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); + try (Connection connection = dataSource.getConnection(); + PreparedStatement stmt = connection.prepareStatement("call drop_partitions_by_max_ttl(?,?,?)")) { stmt.setString(1, partitioning); stmt.setLong(2, systemTtl); stmt.setLong(3, 0); stmt.execute(); printWarnings(stmt); - ResultSet resultSet = stmt.getResultSet(); - resultSet.next(); - log.info("Total partitions removed by TTL: [{}]", resultSet.getLong(1)); + try (ResultSet resultSet = stmt.getResultSet()) { + resultSet.next(); + log.info("Total partitions removed by TTL: [{}]", resultSet.getLong(1)); + } } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); } From 523b118ef219f129e881b181c1d7242b644ddf76 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 9 Jun 2021 13:29:42 +0300 Subject: [PATCH 018/151] Structure for the Lwm2mDeviceProfileTransportConfiguration --- .../Lwm2mDeviceTransportConfiguration.java | 4 +- .../data/lwm2m/BootstrapConfiguration.java | 30 ++++++++++ .../device/data/lwm2m/ObjectAttributes.java | 31 ++++++++++ .../device/data/lwm2m/OtherConfiguration.java | 27 +++++++++ .../lwm2m/TelemetryMappingConfiguration.java | 32 ++++++++++ ...2mDeviceProfileTransportConfiguration.java | 25 +++----- ...java => DefaultLwM2MUplinkMsgHandler.java} | 59 +++++++------------ .../server/DefaultLwM2mTransportService.java | 4 +- .../lwm2m/server/LwM2mServerListener.java | 4 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 4 +- .../server/LwM2mTransportCoapResource.java | 6 +- .../lwm2m/server/LwM2mTransportRequest.java | 2 +- .../lwm2m/server/LwM2mTransportUtil.java | 51 ++++++++-------- ...andler.java => LwM2mUplinkMsgHandler.java} | 2 +- .../lwm2m/server/client/LwM2mClient.java | 4 +- .../server/client/LwM2mClientRpcRequest.java | 12 ++-- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 22 +++---- 17 files changed, 207 insertions(+), 112 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/BootstrapConfiguration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/TelemetryMappingConfiguration.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{DefaultLwM2MTransportMsgHandler.java => DefaultLwM2MUplinkMsgHandler.java} (97%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportMsgHandler.java => LwM2mUplinkMsgHandler.java} (98%) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceTransportConfiguration.java index 14901ef69f..510bfeda7f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/Lwm2mDeviceTransportConfiguration.java @@ -19,8 +19,10 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; -import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; import java.util.HashMap; import java.util.Map; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/BootstrapConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/BootstrapConfiguration.java new file mode 100644 index 0000000000..66bdfd1f4c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/BootstrapConfiguration.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data.lwm2m; + +import lombok.Data; + +import java.util.Map; + +@Data +public class BootstrapConfiguration { + + //TODO: define the objects; + private Map servers; + private Map lwm2mServer; + private Map bootstrapServer; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java new file mode 100644 index 0000000000..4e01f645bc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data.lwm2m; + +import lombok.Data; + +@Data +public class ObjectAttributes { + + private Integer dim; + private String ver; + private Integer pmin; + private Integer pmax; + private Integer gt; + private Integer lt; + private Integer st; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java new file mode 100644 index 0000000000..2bfef74391 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data.lwm2m; + +import lombok.Data; + +@Data +public class OtherConfiguration { + + private Integer fwUpdateStrategy; + private Integer swUpdateStrategy; + private Integer clientOnlyObserveAfterConnect; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/TelemetryMappingConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/TelemetryMappingConfiguration.java new file mode 100644 index 0000000000..2f2683998d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/TelemetryMappingConfiguration.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data.lwm2m; + +import lombok.Data; + +import java.util.Map; +import java.util.Set; + +@Data +public class TelemetryMappingConfiguration { + + private Map keyName; + private Set observe; + private Set attribute; + private Set telemetry; + private Map attributeLwm2m; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java index 34e281fd73..31ae4594ea 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/Lwm2mDeviceProfileTransportConfiguration.java @@ -15,31 +15,20 @@ */ package org.thingsboard.server.common.data.device.profile; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; -import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; - -import java.util.HashMap; -import java.util.Map; +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; @Data public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { - @JsonIgnore - private Map properties = new HashMap<>(); - - @JsonAnyGetter - public Map properties() { - return this.properties; - } + private static final long serialVersionUID = 6257277825459600068L; - @JsonAnySetter - public void put(String name, Object value) { - this.properties.put(name, value); - } + private TelemetryMappingConfiguration observeAttr; + private BootstrapConfiguration bootstrap; + private OtherConfiguration clientLwM2mSettings; @Override public DeviceTransportType getType() { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java similarity index 97% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index 4f97273cb6..c94f44f55c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -89,10 +89,8 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; @@ -125,7 +123,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.v @Slf4j @Service @TbLwM2mTransportComponent -public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler { +public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private ExecutorService registrationExecutor; private ExecutorService updateRegistrationExecutor; @@ -144,11 +142,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler private final Map rpcSubscriptions; public final Map firmwareUpdateState; - public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, - LwM2mClientContext clientContext, - @Lazy LwM2mTransportRequest lwM2mTransportRequest, - OtaPackageDataCache otaPackageDataCache, - LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, + LwM2mClientContext clientContext, + @Lazy LwM2mTransportRequest lwM2mTransportRequest, + OtaPackageDataCache otaPackageDataCache, + LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.config = config; this.helper = helper; @@ -233,6 +231,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler updateRegistrationExecutor.submit(() -> { LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); try { + log.warn("[{}] [{{}] Client: update after Registration", registration.getEndpoint(), registration.getId()); clientContext.updateRegistration(lwM2MClient, registration); TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession(); this.reportActivityAndRegister(sessionInfo); @@ -267,8 +266,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); transportService.deregisterSession(sessionInfo); - this.doCloseSession(sessionInfo); sessionStore.remove(registration.getEndpoint()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { @@ -552,19 +551,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler transportService.deregisterSession(sessionInfo); } - /** - * Session device in thingsboard is closed - * - * @param sessionInfo - lwm2m client - */ - private void doCloseSession(SessionInfoProto sessionInfo) { - TransportProtos.SessionEvent event = SessionEvent.CLOSED; - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder() - .setSessionType(TransportProtos.SessionType.ASYNC) - .setEvent(event).build(); - transportService.process(sessionInfo, msg, null); - } - /** * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, * * if you need to do long time processing use a dedicated thread pool. @@ -609,20 +595,20 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param lwM2MClient - object with All parameters off client */ private void initClientTelemetry(LwM2mClient lwM2MClient) { - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId()); - Set clientObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); - if (clientObjects != null && clientObjects.size() > 0) { - if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == lwM2MClientProfile.getClientStrategy()) { + LwM2mClientProfile profile = clientContext.getProfile(lwM2MClient.getProfileId()); + Set supportedObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); + if (supportedObjects != null && supportedObjects.size() > 0) { + if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientStrategy()) { // #2 - lwM2MClient.getPendingReadRequests().addAll(clientObjects); - clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ, + lwM2MClient.getPendingReadRequests().addAll(supportedObjects); + supportedObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ, null, this.config.getTimeout(), null)); } // #1 - this.initReadAttrTelemetryObserveToClient(lwM2MClient, READ, clientObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, OBSERVE, clientObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, WRITE_ATTRIBUTES, clientObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, DISCOVER, clientObjects); + this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, READ, supportedObjects); + this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, OBSERVE, supportedObjects); + this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, WRITE_ATTRIBUTES, supportedObjects); + this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, DISCOVER, supportedObjects); } } @@ -717,8 +703,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, Set clientObjects) { - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId()); + private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mClientProfile lwM2MClientProfile, LwM2mTypeOper typeOper, Set supportedObjects) { Set result = null; ConcurrentHashMap params = null; if (READ.equals(typeOper)) { @@ -738,7 +723,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); result = params.keySet(); } - sendRequestsToClient(lwM2MClient, typeOper, clientObjects, result, params); + sendRequestsToClient(lwM2MClient, typeOper, supportedObjects, result, params); } private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set supportedObjectIds, Set desiredObjectIds, ConcurrentHashMap params) { @@ -1325,7 +1310,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); if (sessionInfo != null) { - DefaultLwM2MTransportMsgHandler handler = this; + DefaultLwM2MUplinkMsgHandler handler = this; this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), new TransportServiceCallback<>() { @Override @@ -1375,7 +1360,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); if (sessionInfo != null) { - DefaultLwM2MTransportMsgHandler handler = this; + DefaultLwM2MUplinkMsgHandler handler = this; transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), new TransportServiceCallback<>() { @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 9be164023d..c6711c31d3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -26,7 +26,6 @@ import org.eclipse.leshan.server.californium.LeshanServer; import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.model.LwM2mModelProvider; -import org.eclipse.leshan.server.security.EditableSecurityStore; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; @@ -35,7 +34,6 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -81,7 +79,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; private final LwM2mTransportServerHelper helper; - private final DefaultLwM2MTransportMsgHandler handler; + private final DefaultLwM2MUplinkMsgHandler handler; private final CaliforniumRegistrationStore registrationStore; private final TbSecurityStore securityStore; private final LwM2mClientContext lwM2mClientContext; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index b69825843e..594148022c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -32,9 +32,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c @Slf4j public class LwM2mServerListener { - private final LwM2mTransportMsgHandler service; + private final LwM2mUplinkMsgHandler service; - public LwM2mServerListener(LwM2mTransportMsgHandler service) { + public LwM2mServerListener(LwM2mUplinkMsgHandler service) { this.service = service; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index b71de7db1b..39a2194966 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -36,10 +36,10 @@ import java.util.UUID; @Slf4j public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener { - private DefaultLwM2MTransportMsgHandler handler; + private DefaultLwM2MUplinkMsgHandler handler; private TransportProtos.SessionInfoProto sessionInfo; - public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) { + public LwM2mSessionMsgListener(DefaultLwM2MUplinkMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) { this.handler = handler; this.sessionInfo = sessionInfo; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 4e45b172cb..c19859a36d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -37,9 +37,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); - private final LwM2mTransportMsgHandler handler; + private final LwM2mUplinkMsgHandler handler; - public LwM2mTransportCoapResource(LwM2mTransportMsgHandler handler, String name) { + public LwM2mTransportCoapResource(LwM2mUplinkMsgHandler handler, String name) { super(name); this.handler = handler; this.setObservable(true); // enable observing @@ -150,7 +150,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { } private byte[] getOtaData(UUID currentId) { - return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); + return ((DefaultLwM2MUplinkMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 3568000ead..fc9290cab0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -107,7 +107,7 @@ public class LwM2mTransportRequest { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; private final LwM2mClientContext lwM2mClientContext; - private final DefaultLwM2MTransportMsgHandler handler; + private final DefaultLwM2MUplinkMsgHandler handler; @PostConstruct public void init() { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 1d1feab85e..6159a201b4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -770,33 +770,34 @@ public class LwM2mTransportUtil { * "/3_1.0/0": {"gt": 17}, * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} */ + //TODO: refactor this to valid jackson code instead of parsing manually. public static LwM2mClientProfile toLwM2MClientProfile(DeviceProfile deviceProfile) { - if (((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { - Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); - try { - ObjectMapper mapper = new ObjectMapper(); - String profileStr = mapper.writeValueAsString(profile); - JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; - return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; - } catch (IOException e) { - log.error("", e); - } - } +// if (((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { +// Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); +// try { +// ObjectMapper mapper = new ObjectMapper(); +// String profileStr = mapper.writeValueAsString(profile); +// JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; +// return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; +// } catch (IOException e) { +// log.error("", e); +// } +// } return null; } public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { - if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { - Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); - try { - ObjectMapper mapper = new ObjectMapper(); - String bootstrapStr = mapper.writeValueAsString(bootstrap); - JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; - return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; - } catch (IOException e) { - log.error("", e); - } - } +// if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { +// Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); +// try { +// ObjectMapper mapper = new ObjectMapper(); +// String bootstrapStr = mapper.writeValueAsString(bootstrap); +// JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; +// return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; +// } catch (IOException e) { +// log.error("", e); +// } +// } return null; } @@ -1001,12 +1002,12 @@ public class LwM2mTransportUtil { * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); * Attribute [] attrs = {gt, st}; */ - public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) { + public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MUplinkMsgHandler serviceImpl) { AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; } - private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { + private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MUplinkMsgHandler serviceImpl, String target) { List attributeLists = new ArrayList<>(); ObjectMapper oMapper = new ObjectMapper(); Map map = oMapper.convertValue(params, ConcurrentHashMap.class); @@ -1059,7 +1060,7 @@ public class LwM2mTransportUtil { } } - public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { + public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MUplinkMsgHandler serviceImpl, String target) { switch (type) { /** Integer [0:255]; */ case DIMENSION: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java similarity index 98% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java index ee3b07da34..fcccf29f1d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java @@ -28,7 +28,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcReques import java.util.Collection; import java.util.Optional; -public interface LwM2mTransportMsgHandler { +public interface LwM2mUplinkMsgHandler { void onRegistered(Registration registration, Collection previousObsersations); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 78f9ffb349..519f32b076 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -373,7 +373,7 @@ public class LwM2mClient implements Cloneable { .collect(Collectors.toSet()); } - public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String path) { + public void initReadValue(DefaultLwM2MUplinkMsgHandler serviceImpl, String path) { if (path != null) { this.pendingReadRequests.remove(path); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java index 33525426e3..53385b6f61 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java @@ -24,7 +24,7 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; import java.util.Map; import java.util.Objects; @@ -79,7 +79,7 @@ public class LwM2mClientRpcRequest { } public LwM2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, - TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) { + TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MUplinkMsgHandler handler) { this.registration = registration; this.sessionInfo = sessionInfo; this.requestId = requestId; @@ -110,7 +110,7 @@ public class LwM2mClientRpcRequest { .build(); } - private void init(DefaultLwM2MTransportMsgHandler handler) { + private void init(DefaultLwM2MUplinkMsgHandler handler) { try { // #1 if (this.bodyParams.contains(KEY_NAME_KEY)) { @@ -179,7 +179,7 @@ public class LwM2mClientRpcRequest { } } - private void setValidParamsKey(DefaultLwM2MTransportMsgHandler handler) { + private void setValidParamsKey(DefaultLwM2MUplinkMsgHandler handler) { String paramsStr = this.getValueKeyFromBody(PARAMS_KEY); if (paramsStr != null) { String params2Json = @@ -245,7 +245,7 @@ public class LwM2mClientRpcRequest { } private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, - DefaultLwM2MTransportMsgHandler serviceImpl) { + DefaultLwM2MUplinkMsgHandler serviceImpl) { Map paramsIdVer = new ConcurrentHashMap<>(); LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer))); if (targetId.isObjectInstance()) { @@ -272,7 +272,7 @@ public class LwM2mClientRpcRequest { return (ConcurrentHashMap) paramsIdVer; } - private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) { + private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MUplinkMsgHandler handler) { LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo); return lwM2mClient != null ? lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 6b289bac59..a13253282e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -24,7 +24,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -156,7 +156,7 @@ public class LwM2mFwSwUpdate { } } - public void initReadValue(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) { + public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) { if (pathIdVer != null) { this.pendingInfoRequestsStart.remove(pathIdVer); } @@ -176,7 +176,7 @@ public class LwM2mFwSwUpdate { * Send FsSw to Lwm2mClient: * before operation Write: fw_state = DOWNLOADING */ - public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { + public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); @@ -214,7 +214,7 @@ public class LwM2mFwSwUpdate { } } - public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) { + public void sendLogs(DefaultLwM2MUplinkMsgHandler handler, String typeOper, String typeInfo, String msgError) { // this.sendSateOnThingsBoard(handler); String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); @@ -230,7 +230,7 @@ public class LwM2mFwSwUpdate { * fw_state/sw_state = UPDATING * send execute */ - public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { + public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest); } @@ -248,7 +248,7 @@ public class LwM2mFwSwUpdate { * -- 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. * Compare current versions by contains. */ - private boolean conditionalFwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { + private boolean conditionalFwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId); String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId); @@ -313,7 +313,7 @@ public class LwM2mFwSwUpdate { * - If Update Result is not errors and ver is not empty - This means that before unInstall update * * - Check if the version has changed and launch a new update. */ - private boolean conditionalSwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { + private boolean conditionalSwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); // #1/#2 return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || @@ -347,7 +347,7 @@ public class LwM2mFwSwUpdate { * --- send to telemetry ( key - this is name Update Result in model) ( * -- fw_state/sw_state = FAILED */ - public void finishFwSwUpdate(DefaultLwM2MTransportMsgHandler handler, boolean success) { + public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; @@ -381,7 +381,7 @@ public class LwM2mFwSwUpdate { return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; } - private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { + private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { request.sendAllRequest(lwM2MClient, convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, null, null, 0, null); @@ -390,7 +390,7 @@ public class LwM2mFwSwUpdate { null, null, 0, null); } - public void sendSateOnThingsBoard(DefaultLwM2MTransportMsgHandler handler) { + public void sendSateOnThingsBoard(DefaultLwM2MUplinkMsgHandler handler) { if (StringUtils.trimToNull(this.stateUpdate) != null) { List result = new ArrayList<>(); TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE)); @@ -433,7 +433,7 @@ public class LwM2mFwSwUpdate { * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") * - finished operation Execute (FwUpdate) */ - public void updateStateOta(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, + public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request, Registration registration, String path, int value) { if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { From dd748245706903847ba72f7c6066e97e026ebe28 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 9 Jun 2021 13:30:28 +0300 Subject: [PATCH 019/151] js-executor: Refactoring Kafka executor for use async/await --- msa/js-executor/queue/kafkaTemplate.js | 68 +++++++++++--------------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 7fce429d23..b34727c977 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -36,11 +36,10 @@ let producer; const configEntries = []; let batchMessages = []; -let batchResolvers = []; let sendLoopInstance; function KafkaProducer() { - this.send = (responseTopic, scriptId, rawResponse, headers) => { + this.send = async (responseTopic, scriptId, rawResponse, headers) => { logger.debug('Pending queue response, scriptId: [%s]', scriptId); const message = { topic: responseTopic, @@ -51,60 +50,50 @@ function KafkaProducer() { }] }; - return pushMessageToSendLater(message); + await pushMessageToSendLater(message); } } -function pushMessageToSendLater(message) { - let resolver; - const promise = new Promise((resolve, reject) => { - resolver = resolve; - }); +async function pushMessageToSendLater(message) { batchMessages.push(message); - batchResolvers.push(resolver); if (batchMessages.length >= maxBatchSize) { - sendMessagesAsBatch(); - sendLoopWithLinger(); //reset loop function and reschedule new linger + await sendMessagesAsBatch(true); } - return promise; } function sendLoopWithLinger() { if (sendLoopInstance) { - logger.debug("Clear sendLoop scheduler. Starting new send loop with linger [%s]", linger); - clearInterval(sendLoopInstance); + clearTimeout(sendLoopInstance); } else { logger.debug("Starting new send loop with linger [%s]", linger) } - sendLoopInstance = setInterval(sendMessagesAsBatch, linger); + sendLoopInstance = setTimeout(sendMessagesAsBatch, linger); } -function sendMessagesAsBatch() { +async function sendMessagesAsBatch(isImmediately) { + if (sendLoopInstance) { + logger.debug("sendMessagesAsBatch: Clear sendLoop scheduler. Starting new send loop with linger [%s]", linger); + clearTimeout(sendLoopInstance); + } + sendLoopInstance = null; if (batchMessages.length > 0) { - logger.debug('sendMessagesAsBatch, length: [%s]', batchMessages.length); + logger.debug('sendMessagesAsBatch, length: [%s], %s', batchMessages.length, isImmediately ? 'immediately' : ''); const messagesToSend = batchMessages; - const resolvers = batchResolvers; batchMessages = []; - batchResolvers = []; - producer.sendBatch({ - topicMessages: messagesToSend, - acks: acks, - compression: compressionType - }).then( - () => { - logger.debug('Response batch sent to kafka, length: [%s]', messagesToSend.length); - for (let i = 0; i < resolvers.length; i++) { - resolvers[i](); - } - }, - (err) => { - logger.error('Failed batch send to kafka, length: [%s], pending to reprocess msgs', messagesToSend.length); - logger.error(err.stack); - batchMessages = messagesToSend.concat(batchMessages); - batchResolvers = resolvers.concat(batchResolvers); //promises will never be rejected. Will retry forever - } - ); + try { + await producer.sendBatch({ + topicMessages: messagesToSend, + acks: acks, + compression: compressionType + }) + logger.debug('Response batch sent to kafka, length: [%s]', messagesToSend.length); + } catch(err) { + logger.error('Failed batch send to kafka, length: [%s], pending to reprocess msgs', messagesToSend.length); + logger.error(err.stack); + batchMessages = messagesToSend.concat(batchMessages); + } } + sendLoopWithLinger(); } (async () => { @@ -285,9 +274,8 @@ async function disconnectProducer() { producer = null; try { logger.info('Stopping loop...'); - //TODO: send handle msg - clearInterval(sendLoopInstance); - sendMessagesAsBatch(); + clearTimeout(sendLoopInstance); + await sendMessagesAsBatch(); await _producer.disconnect(); logger.info('Kafka Producer stopped.'); } catch (e) { From a7a7dca3b3eb76166cf8ed93bf764f2c8c5935b2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 8 Jun 2021 20:03:45 +0300 Subject: [PATCH 020/151] js-executor: upgraded libraries to fix vulnerability warning (@google-cloud/pubsub", amqplib) --- msa/js-executor/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 75f009880b..280979afa6 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -13,8 +13,8 @@ }, "dependencies": { "@azure/service-bus": "^1.1.9", - "@google-cloud/pubsub": "^2.5.0", - "amqplib": "^0.6.0", + "@google-cloud/pubsub": "^2.12.0", + "amqplib": "^0.8.0", "aws-sdk": "^2.741.0", "azure-sb": "^0.11.1", "config": "^3.3.1", From 98bea8eaf579f6641bc8a3dfa43639901aca7c91 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 8 Jun 2021 20:14:10 +0300 Subject: [PATCH 021/151] js-executor: http livenessProbe added --- msa/js-executor/api/httpServer.js | 46 +++++++++++++++++++ .../config/custom-environment-variables.yml | 1 + msa/js-executor/config/default.yml | 1 + msa/js-executor/package.json | 1 + msa/js-executor/server.js | 2 + 5 files changed, 51 insertions(+) create mode 100644 msa/js-executor/api/httpServer.js diff --git a/msa/js-executor/api/httpServer.js b/msa/js-executor/api/httpServer.js new file mode 100644 index 0000000000..996638c7c3 --- /dev/null +++ b/msa/js-executor/api/httpServer.js @@ -0,0 +1,46 @@ +/* + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ + +const config = require('config'), + logger = require('../config/logger')._logger('httpServer'), + express = require('express'); + +const httpPort = Number(config.get('http_port')); + +const app = express(); + +app.get('/livenessProbe', async (req, res) => { + const date = new Date(); + const message = { now: date.toISOString() }; + res.send(message); +}) + +app.listen(httpPort, () => logger.info(`Started http endpoint on port ${httpPort}. Please, use /livenessProbe !`)) \ No newline at end of file diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index c5eb15e8b9..7773fdaf0c 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -16,6 +16,7 @@ queue_type: "TB_QUEUE_TYPE" #kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" +http_port: "HTTP_PORT" # /livenessProbe js: response_poll_interval: "REMOTE_JS_RESPONSE_POLL_INTERVAL_MS" diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index 5a2c47a675..bcefc1c639 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -16,6 +16,7 @@ queue_type: "kafka" request_topic: "js_eval.requests" +http_port: "8888" # /livenessProbe js: response_poll_interval: "25" diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 280979afa6..ba940ee85a 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -18,6 +18,7 @@ "aws-sdk": "^2.741.0", "azure-sb": "^0.11.1", "config": "^3.3.1", + "express": "^4.17.1", "js-yaml": "^3.14.0", "kafkajs": "^1.15.0", "long": "^4.0.0", diff --git a/msa/js-executor/server.js b/msa/js-executor/server.js index 0c415cc156..7b2e7e59b8 100644 --- a/msa/js-executor/server.js +++ b/msa/js-executor/server.js @@ -51,3 +51,5 @@ switch (serviceType) { process.exit(-1); } +require('./api/httpServer'); + From d109941cf919527db9b574356389f5f71ddef2c5 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Wed, 9 Jun 2021 18:33:20 +0300 Subject: [PATCH 022/151] Redis Security Store implementation, tests for RPK and PSK security and code cleaning --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 94 ++++++++++ .../lwm2m/NoSecLwM2MIntegrationTest.java | 81 +------- .../lwm2m/PskRpkLwm2mIntegrationTest.java | 57 ++++++ .../lwm2m/X509LwM2MIntegrationTest.java | 137 ++------------ .../data/device/credentials/lwm2m/HasKey.java | 21 ++- .../secure/LwM2MBootstrapConfig.java | 3 +- ...LwM2mCredentialsSecurityInfoValidator.java | 8 +- .../lwm2m/secure/TbLwM2MSecurityInfo.java | 4 +- .../lwm2m/secure/TbX509DtlsSessionInfo.java | 4 +- .../store/TbLwM2MDtlsSessionRedisStore.java | 17 +- .../store/TbLwM2mRedisSecurityStore.java | 176 +++++++++--------- .../transport/auth/TransportDeviceInfo.java | 4 +- .../ValidateDeviceCredentialsResponse.java | 4 +- 13 files changed, 291 insertions(+), 319 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 6db6e24f91..8f7d4fb295 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -17,24 +17,43 @@ package org.thingsboard.server.transport.lwm2m; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.commons.io.IOUtils; +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; +import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.SingleEntityFilter; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.controller.AbstractWebsocketTest; import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; +import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import java.io.IOException; import java.io.InputStream; @@ -54,9 +73,13 @@ import java.security.spec.ECPrivateKeySpec; import java.security.spec.ECPublicKeySpec; import java.security.spec.KeySpec; import java.util.Base64; +import java.util.Collections; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @DaoSqlTest public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { @@ -137,6 +160,16 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { // certificates trustedby the server (should contain rootCA) protected final Certificate[] trustedCertificates = new Certificate[1]; + protected final int SECURE_PORT = 5686; + protected final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); + protected final String SECURE_ENDPOINT = "deviceAEndpoint"; + protected final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; + + private final int PORT = 5686; + private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(PORT)); + private final String ENDPOINT = "deviceAEndpoint"; + private final String SERVER_URI = "coaps://localhost:" + PORT; + public AbstractLwM2MIntegrationTest() { // create client credentials try { @@ -260,10 +293,71 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { Assert.assertNotNull(deviceProfile); } + @NotNull + private Device createDevice(LwM2MClientCredentials clientCredentials) throws Exception { + Device device = new Device(); + device.setName("Device A"); + device.setDeviceProfileId(deviceProfile.getId()); + device.setTenantId(tenantId); + device = doPost("/api/device", device, Device.class); + Assert.assertNotNull(device); + + DeviceCredentials deviceCredentials = + doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); + Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); + deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); + + LwM2MCredentials credentials = new LwM2MCredentials(); + + credentials.setClient(clientCredentials); + + deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); + doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); + return device; + } + @After public void after() { executor.shutdownNow(); wsClient.close(); } + public void basicTestConnectionObserveTelemetry(Security security, + LwM2MClientCredentials credentials, + NetworkConfig coapConfig, + String endpoint) throws Exception { + createDeviceProfile(TRANSPORT_CONFIGURATION); + Device device = createDevice(credentials); + + SingleEntityFilter sef = new SingleEntityFilter(); + sef.setSingleEntity(device.getId()); + LatestValueCmd latestCmd = new LatestValueCmd(); + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); + + wsClient.send(mapper.writeValueAsString(wrapper)); + wsClient.waitForReply(); + + wsClient.registerWaitForUpdate(); + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); + + client.init(security, coapConfig); + String msg = wsClient.waitForUpdate(); + + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + Assert.assertEquals(1, update.getCmdId()); + List eData = update.getUpdate(); + Assert.assertNotNull(eData); + Assert.assertEquals(1, eData.size()); + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); + Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); + client.destroy(); + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java index 7d173e61d9..e7a36b75af 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java @@ -17,32 +17,10 @@ package org.thingsboard.server.transport.lwm2m; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; import org.junit.Test; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; -import org.thingsboard.server.common.data.query.EntityData; -import org.thingsboard.server.common.data.query.EntityDataPageLink; -import org.thingsboard.server.common.data.query.EntityDataQuery; -import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.SingleEntityFilter; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.common.data.security.DeviceCredentialsType; -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; -import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; - -import java.util.Collections; -import java.util.List; import static org.eclipse.leshan.client.object.Security.noSec; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @@ -51,64 +29,11 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); private final String ENDPOINT = "deviceAEndpoint"; - @NotNull - private Device createDevice() throws Exception { - Device device = new Device(); - device.setName("Device A"); - device.setDeviceProfileId(deviceProfile.getId()); - device.setTenantId(tenantId); - device = doPost("/api/device", device, Device.class); - Assert.assertNotNull(device); - - DeviceCredentials deviceCredentials = - doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); - Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); - deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); - - LwM2MCredentials noSecCredentials = new LwM2MCredentials(); - NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); - clientCredentials.setEndpoint(ENDPOINT); - noSecCredentials.setClient(clientCredentials); - deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials)); - doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); - return device; - } - @Test public void testConnectAndObserveTelemetry() throws Exception { - createDeviceProfile(TRANSPORT_CONFIGURATION); - - Device device = createDevice(); - - SingleEntityFilter sef = new SingleEntityFilter(); - sef.setSingleEntity(device.getId()); - LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - wsClient.send(mapper.writeValueAsString(wrapper)); - wsClient.waitForReply(); - - wsClient.registerWaitForUpdate(); - LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT); - client.init(SECURITY, COAP_CONFIG); - String msg = wsClient.waitForUpdate(); - - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); - Assert.assertEquals(1, update.getCmdId()); - List eData = update.getUpdate(); - Assert.assertNotNull(eData); - Assert.assertEquals(1, eData.size()); - Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); - Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); - client.destroy(); + NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); + clientCredentials.setEndpoint(ENDPOINT); + super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java new file mode 100644 index 0000000000..0d2f4d05a5 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m; + +import org.eclipse.leshan.client.object.Security; +import org.eclipse.leshan.core.util.Hex; +import org.junit.Test; +import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials; +import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials; + +import java.nio.charset.StandardCharsets; + +import static org.eclipse.leshan.client.object.Security.psk; +import static org.eclipse.leshan.client.object.Security.rpk; + +public class PskRpkLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { + @Test + public void testConnectWithRPKAndObserveTelemetry() throws Exception { + RPKClientCredentials rpkClientCredentials = new RPKClientCredentials(); + rpkClientCredentials.setEndpoint(SECURE_ENDPOINT); + rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded())); + Security security = rpk(SECURE_URI, + 123, + clientPublicKey.getEncoded(), + clientPrivateKey.getEncoded(), + serverX509Cert.getPublicKey().getEncoded()); + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); + } + + @Test + public void testConnectWithPSKAndObserveTelemetry() throws Exception { + String pskIdentity = "SOME_PSK_ID"; + String pskKey = "73656372657450534b"; + PSKClientCredentials clientCredentials = new PSKClientCredentials(); + clientCredentials.setEndpoint(SECURE_ENDPOINT); + clientCredentials.setKey(pskKey); + clientCredentials.setIdentity(pskIdentity); + Security security = psk(SECURE_URI, + 123, + pskIdentity.getBytes(StandardCharsets.UTF_8), + Hex.decodeHex(pskKey.toCharArray())); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java index 0c13c57441..8e3c26298d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java @@ -15,145 +15,36 @@ */ package org.thingsboard.server.transport.lwm2m; -import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; import org.junit.Test; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials; -import org.thingsboard.server.common.data.query.EntityData; -import org.thingsboard.server.common.data.query.EntityDataPageLink; -import org.thingsboard.server.common.data.query.EntityDataQuery; -import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.SingleEntityFilter; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.transport.util.SslUtil; -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; -import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; -import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; -import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; - -import java.util.Collections; -import java.util.List; import static org.eclipse.leshan.client.object.Security.x509; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - - private final int port = 5686; - private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port)); - private final String endpoint = "deviceAEndpoint"; - private final String serverUri = "coaps://localhost:" + port; - - @NotNull - private Device createDevice(X509ClientCredentials clientCredentials) throws Exception { - Device device = new Device(); - device.setName("Device A"); - device.setDeviceProfileId(deviceProfile.getId()); - device.setTenantId(tenantId); - device = doPost("/api/device", device, Device.class); - Assert.assertNotNull(device); - - DeviceCredentials deviceCredentials = - doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class); - Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId()); - deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); - - LwM2MCredentials credentials = new LwM2MCredentials(); - - credentials.setClient(clientCredentials); - - deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials)); - doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); - return device; - } - @Test public void testConnectAndObserveTelemetry() throws Exception { - createDeviceProfile(TRANSPORT_CONFIGURATION); X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(endpoint); - Device device = createDevice(credentials); - - SingleEntityFilter sef = new SingleEntityFilter(); - sef.setSingleEntity(device.getId()); - LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - wsClient.send(mapper.writeValueAsString(wrapper)); - wsClient.waitForReply(); - - wsClient.registerWaitForUpdate(); - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); - Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); - client.init(security, coapConfig); - String msg = wsClient.waitForUpdate(); - - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); - Assert.assertEquals(1, update.getCmdId()); - List eData = update.getUpdate(); - Assert.assertNotNull(eData); - Assert.assertEquals(1, eData.size()); - Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); - Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); - client.destroy(); + credentials.setEndpoint(SECURE_ENDPOINT); + Security security = x509(SECURE_URI, + 123, + clientX509Cert.getEncoded(), + clientPrivateKeyFromCert.getEncoded(), + serverX509Cert.getEncoded()); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); } @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { - createDeviceProfile(TRANSPORT_CONFIGURATION); X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(endpoint); + credentials.setEndpoint(SECURE_ENDPOINT); credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); - Device device = createDevice(credentials); - - SingleEntityFilter sef = new SingleEntityFilter(); - sef.setSingleEntity(device.getId()); - LatestValueCmd latestCmd = new LatestValueCmd(); - latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel"))); - EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), - Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - - EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - wsClient.send(mapper.writeValueAsString(wrapper)); - wsClient.waitForReply(); - - wsClient.registerWaitForUpdate(); - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); - - Security security = x509(serverUri, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); - - client.init(security, coapConfig); - String msg = wsClient.waitForUpdate(); - - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); - Assert.assertEquals(1, update.getCmdId()); - List eData = update.getUpdate(); - Assert.assertNotNull(eData); - Assert.assertEquals(1, eData.size()); - Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); - Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); - var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel"); - Assert.assertEquals(42, Long.parseLong(tsValue.getValue())); - client.destroy(); + Security security = x509(SECURE_URI, + 123, + clientX509CertNotTrusted.getEncoded(), + clientPrivateKeyFromCert.getEncoded(), + serverX509Cert.getEncoded()); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java index ec62765298..58cfa84099 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java @@ -15,20 +15,25 @@ */ package org.thingsboard.server.common.data.device.credentials.lwm2m; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; import lombok.SneakyThrows; import org.apache.commons.codec.binary.Hex; public abstract class HasKey extends AbstractLwM2MClientCredentials { - private byte[] key; + @Getter + @Setter + private String key; + + private byte[] keyInBytes; @SneakyThrows - public void setKey(String key) { - if (key != null) { - this.key = Hex.decodeHex(key.toLowerCase().toCharArray()); + @JsonIgnore + public byte[] getDecodedKey() { + if (keyInBytes == null) { + keyInBytes = Hex.decodeHex(key.toLowerCase().toCharArray()); } - } - - public byte[] getKey() { - return key; + return keyInBytes; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java index 8ffc3ac1f2..30ac8e01c3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapConfig.java @@ -21,10 +21,11 @@ import org.eclipse.leshan.core.request.BindingMode; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.bootstrap.BootstrapConfig; +import java.io.Serializable; import java.nio.charset.StandardCharsets; @Data -public class LwM2MBootstrapConfig { +public class LwM2MBootstrapConfig implements Serializable { /* interface BootstrapSecurityConfig servers: BootstrapServersSecurityConfig, diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 13d6fd6568..cbb7dd15f4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -135,10 +135,10 @@ public class LwM2mCredentialsSecurityInfoValidator { PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig; if (StringUtils.isNotEmpty(pskConfig.getIdentity())) { try { - if (pskConfig.getKey() != null && pskConfig.getKey().length > 0) { + if (pskConfig.getDecodedKey() != null && pskConfig.getDecodedKey().length > 0) { endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint; if (endpoint != null && !endpoint.isEmpty()) { - result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getKey())); + result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getDecodedKey())); result.setSecurityMode(PSK); } } @@ -153,8 +153,8 @@ public class LwM2mCredentialsSecurityInfoValidator { private void createClientSecurityInfoRPK(TbLwM2MSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) { RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig; try { - if (rpkConfig.getKey() != null) { - PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getKey()); + if (rpkConfig.getDecodedKey() != null) { + PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getDecodedKey()); result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key)); result.setSecurityMode(RPK); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java index 9b9147c44f..bc45a77b58 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MSecurityInfo.java @@ -23,8 +23,10 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig; +import java.io.Serializable; + @Data -public class TbLwM2MSecurityInfo { +public class TbLwM2MSecurityInfo implements Serializable { private ValidateDeviceCredentialsResponse msg; private SecurityInfo securityInfo; private SecurityMode securityMode; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java index 1c038a9440..03490fe69d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbX509DtlsSessionInfo.java @@ -18,8 +18,10 @@ package org.thingsboard.server.transport.lwm2m.secure; import lombok.Data; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; +import java.io.Serializable; + @Data -public class TbX509DtlsSessionInfo { +public class TbX509DtlsSessionInfo implements Serializable { private final String x509CommonName; private final ValidateDeviceCredentialsResponse credentials; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java index b1c4b85e2a..616688a620 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MDtlsSessionRedisStore.java @@ -15,28 +15,29 @@ */ package org.thingsboard.server.transport.lwm2m.server.store; -import com.fasterxml.jackson.databind.JsonNode; +import org.nustaq.serialization.FSTConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo; public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore { private static final String SESSION_EP = "SESSION#EP#"; - RedisConnectionFactory connectionFactory; + private final RedisConnectionFactory connectionFactory; + private final FSTConfiguration serializer; public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) { this.connectionFactory = redisConnectionFactory; + this.serializer = FSTConfiguration.createDefaultConfiguration(); } @Override public void put(String endpoint, TbX509DtlsSessionInfo msg) { try (var c = connectionFactory.getConnection()) { - var msgJson = JacksonUtil.convertValue(msg, JsonNode.class); - if (msgJson != null) { - c.set(getKey(endpoint), msgJson.toString().getBytes()); + var serializedMsg = serializer.asByteArray(msg); + if (serializedMsg != null) { + c.set(getKey(endpoint), serializedMsg); } else { - throw new RuntimeException("Problem with serialization of message: " + msg.toString()); + throw new RuntimeException("Problem with serialization of message: " + msg); } } } @@ -46,7 +47,7 @@ public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore { try (var c = connectionFactory.getConnection()) { var data = c.get(getKey(endpoint)); if (data != null) { - return JacksonUtil.fromString(new String(data), TbX509DtlsSessionInfo.class); + return (TbX509DtlsSessionInfo) serializer.asObject(data); } else { return null; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java index 9e3fe5625d..b108286afd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisSecurityStore.java @@ -15,49 +15,55 @@ */ package org.thingsboard.server.transport.lwm2m.server.store; -import org.eclipse.leshan.server.redis.serialization.SecurityInfoSerDes; -import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.SecurityInfo; -import org.eclipse.leshan.server.security.SecurityStoreListener; -import org.springframework.data.redis.connection.RedisClusterConnection; +import org.nustaq.serialization.FSTConfiguration; import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.Cursor; -import org.springframework.data.redis.core.ScanOptions; +import org.springframework.integration.redis.util.RedisLockRegistry; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; +import java.util.concurrent.locks.Lock; public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore { private static final String SEC_EP = "SEC#EP#"; - + private static final String LOCK_EP = "LOCK#EP#"; private static final String PSKID_SEC = "PSKID#SEC"; private final RedisConnectionFactory connectionFactory; - private SecurityStoreListener listener; + private final FSTConfiguration serializer; + private final RedisLockRegistry redisLock; public TbLwM2mRedisSecurityStore(RedisConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; + redisLock = new RedisLockRegistry(connectionFactory, "Security"); + serializer = FSTConfiguration.createDefaultConfiguration(); } @Override public SecurityInfo getByEndpoint(String endpoint) { + Lock lock = null; try (var connection = connectionFactory.getConnection()) { + lock = redisLock.obtain(toLockKey(endpoint)); + lock.lock(); byte[] data = connection.get((SEC_EP + endpoint).getBytes()); if (data == null) { return null; } else { - return deserialize(data); + return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo(); + } + } finally { + if (lock != null) { + lock.unlock(); } } } @Override public SecurityInfo getByIdentity(String identity) { + Lock lock = null; try (var connection = connectionFactory.getConnection()) { + lock = redisLock.obtain(toLockKey(identity)); + lock.lock(); byte[] ep = connection.hGet(PSKID_SEC.getBytes(), identity.getBytes()); if (ep == null) { return null; @@ -66,102 +72,86 @@ public class TbLwM2mRedisSecurityStore implements TbEditableSecurityStore { if (data == null) { return null; } else { - return deserialize(data); + return ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo(); } } + } finally { + if (lock != null) { + lock.unlock(); + } } } @Override public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { - //TODO: implement + SecurityInfo info = tbSecurityInfo.getSecurityInfo(); + byte[] tbSecurityInfoSerialized = serializer.asByteArray(tbSecurityInfo); + Lock lock = null; + try (var connection = connectionFactory.getConnection()) { + lock = redisLock.obtain(tbSecurityInfo.getEndpoint()); + lock.lock(); + if (info != null && info.getIdentity() != null) { + byte[] oldEndpointBytes = connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes()); + if (oldEndpointBytes != null) { + String oldEndpoint = new String(oldEndpointBytes); + if (!oldEndpoint.equals(info.getEndpoint())) { + throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used"); + } + connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes()); + } + } + + byte[] previousData = connection.getSet((SEC_EP + tbSecurityInfo.getEndpoint()).getBytes(), tbSecurityInfoSerialized); + if (previousData != null && info != null) { + String previousIdentity = ((TbLwM2MSecurityInfo) serializer.asObject(previousData)).getSecurityInfo().getIdentity(); + if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) { + connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes()); + } + } + } finally { + if (lock != null) { + lock.unlock(); + } + } } @Override public TbLwM2MSecurityInfo getTbLwM2MSecurityInfoByEndpoint(String endpoint) { - //TODO: implement - return null; + Lock lock = null; + try (var connection = connectionFactory.getConnection()) { + lock = redisLock.obtain(endpoint); + lock.lock(); + byte[] data = connection.get((SEC_EP + endpoint).getBytes()); + return (TbLwM2MSecurityInfo) serializer.asObject(data); + } finally { + if (lock != null) { + lock.unlock(); + } + } } @Override public void remove(String endpoint) { - //TODO: implement - } - - // @Override -// public Collection getAll() { -// try (var connection = connectionFactory.getConnection()) { -// Collection list = new LinkedList<>(); -// ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(SEC_EP + "*").build(); -// List> scans = new ArrayList<>(); -// if (connection instanceof RedisClusterConnection) { -// ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> { -// scans.add(((RedisClusterConnection) connection).scan(node, scanOptions)); -// }); -// } else { -// scans.add(connection.scan(scanOptions)); -// } -// -// scans.forEach(scan -> { -// scan.forEachRemaining(key -> { -// byte[] element = connection.get(key); -// list.add(deserialize(element)); -// }); -// }); -// return list; -// } -// } -// -// @Override -// public SecurityInfo add(SecurityInfo info) throws NonUniqueSecurityInfoException { -// byte[] data = serialize(info); -// try (var connection = connectionFactory.getConnection()) { -// if (info.getIdentity() != null) { -// // populate the secondary index (security info by PSK id) -// String oldEndpoint = new String(connection.hGet(PSKID_SEC.getBytes(), info.getIdentity().getBytes())); -// if (!oldEndpoint.equals(info.getEndpoint())) { -// throw new NonUniqueSecurityInfoException("PSK Identity " + info.getIdentity() + " is already used"); -// } -// connection.hSet(PSKID_SEC.getBytes(), info.getIdentity().getBytes(), info.getEndpoint().getBytes()); -// } -// -// byte[] previousData = connection.getSet((SEC_EP + info.getEndpoint()).getBytes(), data); -// SecurityInfo previous = previousData == null ? null : deserialize(previousData); -// String previousIdentity = previous == null ? null : previous.getIdentity(); -// if (previousIdentity != null && !previousIdentity.equals(info.getIdentity())) { -// connection.hDel(PSKID_SEC.getBytes(), previousIdentity.getBytes()); -// } -// -// return previous; -// } -// } -// -// @Override -// public SecurityInfo remove(String endpoint, boolean infosAreCompromised) { -// try (var connection = connectionFactory.getConnection()) { -// byte[] data = connection.get((SEC_EP + endpoint).getBytes()); -// -// if (data != null) { -// SecurityInfo info = deserialize(data); -// if (info.getIdentity() != null) { -// connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes()); -// } -// connection.del((SEC_EP + endpoint).getBytes()); -// if (listener != null) { -// listener.securityInfoRemoved(infosAreCompromised, info); -// } -// return info; -// } -// } -// return null; -// } - - private byte[] serialize(SecurityInfo secInfo) { - return SecurityInfoSerDes.serialize(secInfo); + Lock lock = null; + try (var connection = connectionFactory.getConnection()) { + lock = redisLock.obtain(endpoint); + lock.lock(); + byte[] data = connection.get((SEC_EP + endpoint).getBytes()); + if (data != null) { + SecurityInfo info = ((TbLwM2MSecurityInfo) serializer.asObject(data)).getSecurityInfo(); + if (info != null && info.getIdentity() != null) { + connection.hDel(PSKID_SEC.getBytes(), info.getIdentity().getBytes()); + } + connection.del((SEC_EP + endpoint).getBytes()); + } + } finally { + if (lock != null) { + lock.unlock(); + } + } } - private SecurityInfo deserialize(byte[] data) { - return SecurityInfoSerDes.deserialize(data); + private String toLockKey(String endpoint) { + return LOCK_EP + endpoint; } - } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java index 27d42d867f..433a5af8ee 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/TransportDeviceInfo.java @@ -21,8 +21,10 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import java.io.Serializable; + @Data -public class TransportDeviceInfo { +public class TransportDeviceInfo implements Serializable { private TenantId tenantId; private CustomerId customerId; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java index e1324791b3..d54dce18fb 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/auth/ValidateDeviceCredentialsResponse.java @@ -19,9 +19,11 @@ import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.DeviceProfile; +import java.io.Serializable; + @Data @Builder -public class ValidateDeviceCredentialsResponse implements DeviceProfileAware { +public class ValidateDeviceCredentialsResponse implements DeviceProfileAware, Serializable { private final TransportDeviceInfo deviceInfo; private final DeviceProfile deviceProfile; From 38d3b9c2936140159b93d2033abbe64ec6cb911a Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Wed, 9 Jun 2021 18:47:45 +0300 Subject: [PATCH 023/151] Constants improvement --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 17 +++++++++-------- .../lwm2m/NoSecLwM2MIntegrationTest.java | 9 --------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 8f7d4fb295..39b4d5ef2f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -78,6 +78,7 @@ import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import static org.eclipse.leshan.client.object.Security.noSec; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -160,15 +161,15 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { // certificates trustedby the server (should contain rootCA) protected final Certificate[] trustedCertificates = new Certificate[1]; - protected final int SECURE_PORT = 5686; - protected final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); - protected final String SECURE_ENDPOINT = "deviceAEndpoint"; - protected final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; + protected static final int SECURE_PORT = 5686; + protected static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); + protected static final String SECURE_ENDPOINT = "deviceAEndpoint"; + protected static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; - private final int PORT = 5686; - private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(PORT)); - private final String ENDPOINT = "deviceAEndpoint"; - private final String SERVER_URI = "coaps://localhost:" + PORT; + protected static final int PORT = 5685; + protected static final Security SECURITY = noSec("coap://localhost:" + PORT, 123); + protected static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); + protected static final String ENDPOINT = "deviceAEndpoint"; public AbstractLwM2MIntegrationTest() { // create client credentials diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java index e7a36b75af..233898e81c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java @@ -15,20 +15,11 @@ */ package org.thingsboard.server.transport.lwm2m; -import org.eclipse.californium.core.network.config.NetworkConfig; -import org.eclipse.leshan.client.object.Security; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; -import static org.eclipse.leshan.client.object.Security.noSec; - public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - private final int PORT = 5685; - private final Security SECURITY = noSec("coap://localhost:" + PORT, 123); - private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); - private final String ENDPOINT = "deviceAEndpoint"; - @Test public void testConnectAndObserveTelemetry() throws Exception { NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); From 3012e5e545ba266e7f8326c1aa8749a06a5f85c3 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 9 Jun 2021 19:02:03 +0300 Subject: [PATCH 024/151] Refactoring of the Device profile update --- .../device/data/lwm2m/ObjectAttributes.java | 14 +- .../secure/LwM2MBootstrapSecurityStore.java | 51 ++- .../server/DefaultLwM2MUplinkMsgHandler.java | 317 ++++++++---------- .../lwm2m/server/LwM2mTransportRequest.java | 130 +++++-- .../lwm2m/server/LwM2mTransportUtil.java | 167 +++------ .../server/LwM2mVersionedModelProvider.java | 2 +- .../lwm2m/server/client/LwM2mClient.java | 49 +-- .../server/client/LwM2mClientContext.java | 14 +- .../server/client/LwM2mClientContextImpl.java | 40 +-- .../server/client/LwM2mClientProfile.java | 113 ------- .../server/client/LwM2mClientRpcRequest.java | 10 +- ...ters.java => ParametersAnalyzeResult.java} | 4 +- 12 files changed, 369 insertions(+), 542 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/{ResultsAnalyzerParameters.java => ParametersAnalyzeResult.java} (92%) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java index 4e01f645bc..ee54a23a07 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java @@ -15,17 +15,19 @@ */ package org.thingsboard.server.common.data.device.data.lwm2m; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; @Data +@JsonInclude(JsonInclude.Include.NON_NULL) public class ObjectAttributes { - private Integer dim; + private Long dim; private String ver; - private Integer pmin; - private Integer pmax; - private Integer gt; - private Integer lt; - private Integer st; + private Long pmin; + private Long pmax; + private Double gt; + private Double lt; + private Double st; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 197c3ba842..e9b0edccf3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -29,6 +29,8 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; @@ -151,35 +153,30 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { } private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) { - try { - LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); - if (lwM2MBootstrapConfig != null) { - ObjectMapper mapper = new ObjectMapper(); - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class); - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); - UUID sessionUUiD = UUID.randomUUID(); - TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); - if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); - String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint()); - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); - return lwM2MBootstrapConfig; - } else { - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); - log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); - String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); - return null; - } + LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); + if (lwM2MBootstrapConfig != null) { + BootstrapConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); + lwM2MBootstrapConfig.servers = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class); + LwM2MServerBootstrap profileServerBootstrap = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class); + LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); + UUID sessionUUiD = UUID.randomUUID(); + TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { + lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); + lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); + return lwM2MBootstrapConfig; + } else { + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); + return null; } - } catch (JsonProcessingException e) { - log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndpoint(), e.getMessage()); - return null; } + log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint()); return null; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index c94f44f55c..7653a2c099 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -15,10 +15,8 @@ */ package org.thingsboard.server.transport.lwm2m.server; -import com.fasterxml.jackson.core.type.TypeReference; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; @@ -36,15 +34,16 @@ import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; -import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -61,12 +60,11 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; +import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -82,16 +80,14 @@ import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; -import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; @@ -102,18 +98,15 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper; @@ -467,7 +460,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { try { LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); Registration registration = client.getRegistration(); - if(registration != null) { + if (registration != null) { lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); if (lwm2mClientRpcRequest.getErrorMsg() != null) { lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); @@ -595,20 +588,72 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param lwM2MClient - object with All parameters off client */ private void initClientTelemetry(LwM2mClient lwM2MClient) { - LwM2mClientProfile profile = clientContext.getProfile(lwM2MClient.getProfileId()); + Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId()); Set supportedObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); if (supportedObjects != null && supportedObjects.size() > 0) { - if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientStrategy()) { + if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { // #2 lwM2MClient.getPendingReadRequests().addAll(supportedObjects); supportedObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ, null, this.config.getTimeout(), null)); } // #1 - this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, READ, supportedObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, OBSERVE, supportedObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, WRITE_ATTRIBUTES, supportedObjects); - this.initReadAttrTelemetryObserveToClient(lwM2MClient, profile, DISCOVER, supportedObjects); + this.sendReadRequests(lwM2MClient, profile, supportedObjects); + this.sendObserveRequests(lwM2MClient, profile, supportedObjects); + this.sendWriteAttributeRequests(lwM2MClient, profile, supportedObjects); +// Removed. Used only for debug. +// this.sendDiscoverRequests(lwM2MClient, profile, supportedObjects); + } + } + + private void sendReadRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { + Set targetIds = new HashSet<>(profile.getObserveAttr().getAttribute()); + targetIds.addAll(profile.getObserveAttr().getTelemetry()); + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); + lwM2MClient.getPendingReadRequests().addAll(targetIds); + targetIds.forEach(targetId -> lwM2mTransportRequest.sendReadRequest(lwM2MClient, targetId, this.config.getTimeout())); + } + + private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { + Set targetIds = profile.getObserveAttr().getObserve(); + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); +// TODO: why do we need to put observe into pending read requests? +// lwM2MClient.getPendingReadRequests().addAll(targetIds); + targetIds.forEach(targetId -> lwM2mTransportRequest.sendObserveRequest(lwM2MClient, targetId, this.config.getTimeout())); + } + + private void sendWriteAttributeRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { + Map attributesMap = profile.getObserveAttr().getAttributeLwm2m(); + attributesMap = attributesMap.entrySet().stream().filter(target -> isSupportedTargetId(supportedObjects, target.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); +// TODO: why do we need to put observe into pending read requests? +// lwM2MClient.getPendingReadRequests().addAll(targetIds); + attributesMap.forEach((targetId, params) -> lwM2mTransportRequest.sendWriteAttributesRequest(lwM2MClient, targetId, params, this.config.getTimeout())); + } + + + private void sendDiscoverRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { + Set targetIds = profile.getObserveAttr().getAttributeLwm2m().keySet(); + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); +// TODO: why do we need to put observe into pending read requests? +// lwM2MClient.getPendingReadRequests().addAll(targetIds); + targetIds.forEach(targetId -> lwM2mTransportRequest.sendDiscoverRequest(lwM2MClient, targetId, this.config.getTimeout())); + } + + private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set supportedObjectIds, Set desiredObjectIds, ConcurrentHashMap params) { + if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) { + Set targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target) + ).collect(Collectors.toUnmodifiableSet()); + if (!targetObjectIds.isEmpty()) { + //TODO: remove this side effect? + lwM2MClient.getPendingReadRequests().addAll(targetObjectIds); + targetObjectIds.forEach(target -> { + Object additionalParams = params != null ? params.get(target) : null; + lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); + }); + if (OBSERVE.equals(operationType)) { + lwM2MClient.initReadValue(this, null); + } + } } } @@ -618,7 +663,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param pathIdVer - */ private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String pathIdVer) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObject.getInstances().forEach((instanceId, instance) -> { String pathInstance = pathIds.toString() + "/" + instanceId; this.updateObjectInstanceResourceValue(registration, instance, pathInstance); @@ -631,7 +676,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param pathIdVer - */ private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { String pathRez = pathIds.toString() + "/" + resourceId; this.updateResourcesValue(registration, resource, pathRez); @@ -668,9 +713,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); fwUpdate.updateStateOta(this, lwM2mTransportRequest, registration, path, ((Long) lwM2mResource.getValue()).intValue()); } - Set paths = new HashSet<>(); - paths.add(path); - this.updateAttrTelemetry(registration, paths); + this.updateAttrTelemetry(registration, Collections.singleton(path)); } else { log.error("Fail update Resource [{}]", lwM2mResource); } @@ -703,47 +746,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mClientProfile lwM2MClientProfile, LwM2mTypeOper typeOper, Set supportedObjects) { - Set result = null; - ConcurrentHashMap params = null; - if (READ.equals(typeOper)) { - result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(), - new TypeReference<>() { - }); - result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(), - new TypeReference<>() { - })); - } else if (OBSERVE.equals(typeOper)) { - result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(), - new TypeReference<>() { - }); - } else if (DISCOVER.equals(typeOper)) { - result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet(); - } else if (WRITE_ATTRIBUTES.equals(typeOper)) { - params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); - result = params.keySet(); - } - sendRequestsToClient(lwM2MClient, typeOper, supportedObjects, result, params); - } - - private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set supportedObjectIds, Set desiredObjectIds, ConcurrentHashMap params) { - if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) { - Set targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target) - ).collect(Collectors.toUnmodifiableSet()); - if (!targetObjectIds.isEmpty()) { - //TODO: remove this side effect? - lwM2MClient.getPendingReadRequests().addAll(targetObjectIds); - targetObjectIds.forEach(target -> { - Object additionalParams = params != null ? params.get(target) : null; - lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); - }); - if (OBSERVE.equals(operationType)) { - lwM2MClient.initReadValue(this, null); - } - } - } - } - private boolean isSupportedTargetId(Set supportedIds, String targetId) { String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH); if (targetIdParts.length <= 1) { @@ -781,20 +783,20 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set path) { if (path != null && path.size() > 0) { ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration); + var profile = clientContext.getProfile(registration); List resultAttributes = new ArrayList<>(); - lwM2MClientProfile.getPostAttributeProfile().forEach(pathIdVer -> { - if (path.contains(pathIdVer.getAsString())) { - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration); + profile.getObserveAttr().getAttribute().forEach(pathIdVer -> { + if (path.contains(pathIdVer)) { + TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer, registration); if (kvAttr != null) { resultAttributes.add(kvAttr); } } }); List resultTelemetries = new ArrayList<>(); - lwM2MClientProfile.getPostTelemetryProfile().forEach(pathIdVer -> { - if (path.contains(pathIdVer.getAsString())) { - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration); + profile.getObserveAttr().getTelemetry().forEach(pathIdVer -> { + if (path.contains(pathIdVer)) { + TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer, registration); if (kvAttr != null) { resultTelemetries.add(kvAttr); } @@ -813,12 +815,12 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) { LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); - JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); - if (names != null && names.has(pathIdVer)) { - String resourceName = names.get(pathIdVer).getAsString(); + Map names = clientContext.getProfile(lwM2MClient.getProfileId()).getObserveAttr().getKeyName(); + if (names != null && names.containsKey(pathIdVer)) { + String resourceName = names.get(pathIdVer); if (resourceName != null && !resourceName.isEmpty()) { try { - LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null; + LwM2mResource resourceValue = getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); if (resourceValue != null) { ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); @@ -830,14 +832,14 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { ResourceModel.Type finalCurrentType = currentType; resourceValue.getInstances().forEach((k, v) -> { Object val = this.converter.convertValue(v, finalCurrentType, expectedType, - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); JsonElement element = gson.toJsonTree(val, val.getClass()); ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element); }); valueKvProto = gson.toJson(valueKvProto); } else { valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); } LwM2mOtaConvert lwM2mOtaConvert = convertOtaUpdateValueToString(pathIdVer, valueKvProto, currentType); valueKvProto = lwM2mOtaConvert.getValue(); @@ -864,7 +866,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); } else { return null; } @@ -879,7 +881,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { LwM2mResource lwm2mResourceValue = null; ResourceValue resourceValue = lwM2MClient.getResources().get(path); if (resourceValue != null) { - if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) { + if (new LwM2mPath(fromVersionedIdToObjectId(path)).isResource()) { lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); } } @@ -929,53 +931,42 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param clients - * @param deviceProfile - */ + //TODO: review and optimize the logic to minimize number of the requests to device. private void onDeviceProfileUpdate(List clients, DeviceProfile deviceProfile) { - LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); + var oldProfile = clientContext.getProfile(deviceProfile.getUuidId()); if (clientContext.profileUpdate(deviceProfile) != null) { // #1 - JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile(); - Set attributeSetOld = convertJsonArrayToSet(attributeOld); - JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile(); - Set telemetrySetOld = convertJsonArrayToSet(telemetryOld); - JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile(); - JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile(); - JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile(); - - LwM2mClientProfile lwM2MClientProfileNew = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); - JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile(); - Set attributeSetNew = convertJsonArrayToSet(attributeNew); - JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile(); - Set telemetrySetNew = convertJsonArrayToSet(telemetryNew); - JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile(); - JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile(); - JsonObject attributeLwm2mNew = lwM2MClientProfileNew.getPostAttributeLwm2mProfile(); + Set attributeSetOld = oldProfile.getObserveAttr().getAttribute(); + Set telemetrySetOld = oldProfile.getObserveAttr().getTelemetry(); + Set observeOld = oldProfile.getObserveAttr().getObserve(); + Map keyNameOld = oldProfile.getObserveAttr().getKeyName(); + Map attributeLwm2mOld = oldProfile.getObserveAttr().getAttributeLwm2m(); + + var newProfile = clientContext.getProfile(deviceProfile.getUuidId()); + Set attributeSetNew = newProfile.getObserveAttr().getAttribute(); + Set telemetrySetNew = newProfile.getObserveAttr().getTelemetry(); + Set observeNew = newProfile.getObserveAttr().getObserve(); + Map keyNameNew = newProfile.getObserveAttr().getKeyName(); + Map attributeLwm2mNew = newProfile.getObserveAttr().getAttributeLwm2m(); // #3 - ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters(); + ParametersAnalyzeResult diff = new ParametersAnalyzeResult(); // #3.1 - if (!attributeOld.equals(attributeNew)) { - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, - new TypeToken>() { - }.getType()), attributeSetNew); - sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); - sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); + if (!attributeSetOld.equals(attributeSetNew)) { + ParametersAnalyzeResult postAttributeAnalyzer = this.getAnalyzerParameters(attributeSetOld, attributeSetNew); + diff.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); + diff.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); } // #3.2 - if (!telemetryOld.equals(telemetryNew)) { - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, - new TypeToken>() { - }.getType()), telemetrySetNew); - sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); - sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); + if (!telemetrySetOld.equals(telemetrySetNew)) { + ParametersAnalyzeResult postTelemetryAnalyzer = this.getAnalyzerParameters(telemetrySetOld, telemetrySetNew); + diff.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); + diff.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); } // #3.3 if (!keyNameOld.equals(keyNameNew)) { - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), - new TypeToken>() { - }.getType()), - new Gson().fromJson(keyNameNew.toString(), new TypeToken>() { - }.getType())); - sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); + ParametersAnalyzeResult keyNameChange = this.getAnalyzerKeyName(keyNameOld, keyNameNew); + diff.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); } // #3.4, #6 @@ -984,32 +975,28 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } // #4.1 add - if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { + if (diff.getPathPostParametersAdd().size() > 0) { // update value in Resources clients.forEach(client -> { - this.readObserveFromProfile(client, sendAttrToThingsboard.getPathPostParametersAdd(), READ); + this.readObserveFromProfile(client, diff.getPathPostParametersAdd(), READ); }); } // #4.2 del - if (sendAttrToThingsboard.getPathPostParametersDel().size() > 0) { - ResultsAnalyzerParameters sendAttrToThingsboardDel = this.getAnalyzerParameters(sendAttrToThingsboard.getPathPostParametersAdd(), sendAttrToThingsboard.getPathPostParametersDel()); - sendAttrToThingsboard.setPathPostParametersDel(sendAttrToThingsboardDel.getPathPostParametersDel()); + if (diff.getPathPostParametersDel().size() > 0) { + ParametersAnalyzeResult sendAttrToThingsboardDel = this.getAnalyzerParameters(diff.getPathPostParametersAdd(), diff.getPathPostParametersDel()); + diff.setPathPostParametersDel(sendAttrToThingsboardDel.getPathPostParametersDel()); } // #5.1 if (!observeOld.equals(observeNew)) { - Set observeSetOld = new Gson().fromJson(observeOld, new TypeToken>() { - }.getType()); - Set observeSetNew = new Gson().fromJson(observeNew, new TypeToken>() { - }.getType()); //#5.2 add // path Attr/Telemetry includes newObserve attributeSetOld.addAll(telemetrySetOld); - ResultsAnalyzerParameters sendObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe + ParametersAnalyzeResult sendObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeOld); // add observe attributeSetNew.addAll(telemetrySetNew); - ResultsAnalyzerParameters sendObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe + ParametersAnalyzeResult sendObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeNew); // add observe // does not include oldObserve - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); + ParametersAnalyzeResult postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); // send Request observe to Client clients.forEach(client -> { Registration registration = client.getRegistration(); @@ -1033,10 +1020,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param parametersNew - * @return ResultsAnalyzerParameters: add && new */ - private ResultsAnalyzerParameters getAnalyzerParameters(Set parametersOld, Set parametersNew) { - ResultsAnalyzerParameters analyzerParameters = null; + private ParametersAnalyzeResult getAnalyzerParameters(Set parametersOld, Set parametersNew) { + ParametersAnalyzeResult analyzerParameters = null; if (!parametersOld.equals(parametersNew)) { - analyzerParameters = new ResultsAnalyzerParameters(); + analyzerParameters = new ParametersAnalyzeResult(); analyzerParameters.setPathPostParametersAdd(parametersNew .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet())); analyzerParameters.setPathPostParametersDel(parametersOld @@ -1045,8 +1032,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return analyzerParameters; } - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) { - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); + private ParametersAnalyzeResult getAnalyzerParametersIn(Set parametersObserve, Set parameters) { + ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); analyzerParameters.setPathPostParametersAdd(parametersObserve .stream().filter(parameters::contains).collect(Collectors.toSet())); return analyzerParameters; @@ -1060,7 +1047,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { */ private void readObserveFromProfile(LwM2mClient client, Set targets, LwM2mTypeOper typeOper) { targets.forEach(target -> { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(target)); if (pathIds.isResource()) { if (READ.equals(typeOper)) { lwM2mTransportRequest.sendAllRequest(client, target, typeOper, @@ -1073,8 +1060,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { }); } - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap keyNameOld, ConcurrentHashMap keyNameNew) { - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); + private ParametersAnalyzeResult getAnalyzerKeyName(Map keyNameOld, Map keyNameNew) { + ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); Set paths = keyNameNew.entrySet() .stream() .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) @@ -1084,23 +1071,12 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } /** - * #3.4, #6 - * #6 * #6.1 - send update WriteAttribute * #6.2 - send empty WriteAttribute * - * @param attributeLwm2mOld - - * @param attributeLwm2mNew - - * @return */ - private void getAnalyzerAttributeLwm2m(List clients, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); - ConcurrentHashMap lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), - new TypeToken>() { - }.getType()); - ConcurrentHashMap lwm2mAttributesNew = new Gson().fromJson(attributeLwm2mNew.toString(), - new TypeToken>() { - }.getType()); + private void getAnalyzerAttributeLwm2m(List clients, Map lwm2mAttributesOld, Map lwm2mAttributesNew) { + ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); Set pathOld = lwm2mAttributesOld.keySet(); Set pathNew = lwm2mAttributesNew.keySet(); analyzerParameters.setPathPostParametersAdd(pathNew @@ -1108,7 +1084,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { analyzerParameters.setPathPostParametersDel(pathOld .stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet())); Set pathCommon = pathNew - .stream().filter(p -> pathOld.contains(p)).collect(Collectors.toSet()); + .stream().filter(pathOld::contains).collect(Collectors.toSet()); Set pathCommonChange = pathCommon .stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet()); analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange); @@ -1120,30 +1096,21 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - ConcurrentHashMap finalParams = lwm2mAttributesNew; - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, - finalParams.get(target), this.config.getTimeout(), null)); + pathSend.forEach(target -> lwM2mTransportRequest.sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target), this.config.getTimeout())); } }); } // #6.2 if (analyzerParameters.getPathPostParametersDel().size() > 0) { clients.forEach(client -> { - Registration registration = client.getRegistration(); Set clientObjects = clientContext.getSupportedIdVerInClient(client); Set pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - pathSend.forEach(target -> { - Map params = (Map) lwm2mAttributesOld.get(target); - params.clear(); - params.put(OBJECT_VERSION, ""); - lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, params, this.config.getTimeout(), null); - }); + pathSend.forEach(target -> lwM2mTransportRequest.sendWriteAttributesRequest(client, target, new ObjectAttributes(), this.config.getTimeout())); } }); } - } private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set paramAnallyzer) { @@ -1183,10 +1150,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @return - */ public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { - LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); + var profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo); - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() - .filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey) + return profile.getObserveAttr().getKeyName().entrySet().stream() + .filter(e -> e.getValue().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey) .orElse(null); } @@ -1292,7 +1259,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); if (sessionInfo != null) { //#1.1 - ConcurrentMap keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); + Map keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); if (keyNamesMap.values().size() > 0) { try { //#1.2 @@ -1401,25 +1368,15 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { .build(); } - /** - * !!! sharedAttr === profileAttr !!! - * Get names or keyNames from profile: resources IsWritable - * - * @param lwM2MClient - - * @return ArrayList keyNames from profile profileAttr && IsWritable - */ - private ConcurrentMap getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { - - LwM2mClientProfile profile = clientContext.getProfile(lwM2MClient.getProfileId()); - return new Gson().fromJson(profile.getPostKeyNameProfile().toString(), - new TypeToken>() { - }.getType()); + private Map getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { + Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId()); + return profile.getObserveAttr().getKeyName(); } private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config .getModelProvider()); - Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId(); + Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); String objectVer = validateObjectVerFromKey(pathIdVer); return resourceModel != null && (isWritableNotOptional ? objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index fc9290cab0..a1c4227728 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -20,6 +20,8 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Response; import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.attributes.Attribute; +import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mObject; @@ -51,6 +53,8 @@ import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -62,15 +66,26 @@ import javax.annotation.PostConstruct; import java.util.Arrays; import java.util.Collection; import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; +import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; +import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; +import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; +import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; +import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; +import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; +import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; @@ -91,7 +106,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest; @@ -116,33 +131,105 @@ public class LwM2mTransportRequest { new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); } + public void sendReadRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { + sendReadRequest(lwM2MClient, targetId, lwM2MClient.getDefaultContentFormat(), timeout); + } + + public void sendReadRequest(LwM2mClient lwM2MClient, String targetId, ContentFormat contentFormat, Long timeout) { + String objectId = fromVersionedIdToObjectId(targetId); + if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { + sendRequest(lwM2MClient, new ReadRequest(contentFormat, objectId), timeout); + } + } + + public void sendObserveRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { + sendObserveRequest(lwM2MClient, targetId, lwM2MClient.getDefaultContentFormat(), timeout); + } + + public void sendObserveRequest(LwM2mClient lwM2MClient, String targetId, ContentFormat contentFormat, Long timeout) { + String objectId = fromVersionedIdToObjectId(targetId); + if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { + LwM2mPath resultIds = new LwM2mPath(objectId); + Set observations = context.getServer().getObservationService().getObservations(lwM2MClient.getRegistration()); + if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { + ObserveRequest request; + if (resultIds.isResource()) { + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + } else if (resultIds.isObjectInstance()) { + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); + } else { + request = new ObserveRequest(contentFormat, resultIds.getObjectId()); + } + log.info("[{}] Send observation: {}.", lwM2MClient.getEndpoint(), targetId); + sendRequest(lwM2MClient, request, timeout); + } + } + } + + public void sendDiscoverRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { + String objectId = fromVersionedIdToObjectId(targetId); + if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { + sendRequest(lwM2MClient, new DiscoverRequest(objectId), timeout); + } + } + + public void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params, Long timeout) { + String objectId = fromVersionedIdToObjectId(targetId); + if (objectId != null && lwM2MClient.isValidObjectVersion(targetId) && params != null) { + List attributes = new LinkedList<>(); +// Dimension and Object version are read only attributes. +// addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); +// addAttribute(attributes, OBJECT_VERSION, params.getVer(), StringUtils::isNotEmpty, Function.identity()); + addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); + addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); + addAttribute(attributes, GREATER_THAN, params.getGt()); + addAttribute(attributes, LESSER_THAN, params.getLt()); + addAttribute(attributes, STEP, params.getSt()); + AttributeSet attributeSet = new AttributeSet(attributes); + sendRequest(lwM2MClient, new WriteAttributesRequest(objectId, attributeSet), timeout); + } + } + + private void addAttribute(List attributes, String attributeName, T value) { + addAttribute(attributes, attributeName, value, null, null); + } + + private void addAttribute(List attributes, String attributeName, T value, Function converter) { + addAttribute(attributes, attributeName, value, null, converter); + } + + private void addAttribute(List attributes, String attributeName, T value, Predicate filter, Function converter) { + if (value != null && ((filter == null) || filter.test(value))) { + attributes.add(new Attribute(attributeName, converter != null ? converter.apply(value) : value)); + } + } + public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest); } - public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { Registration registration = lwM2MClient.getRegistration(); try { - String target = convertPathFromIdVerToObjectId(targetIdVer); - if(contentFormat == null){ + String target = fromVersionedIdToObjectId(targetIdVer); + if (contentFormat == null) { contentFormat = ContentFormat.DEFAULT; } LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; - if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { + if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0) { if (lwM2MClient.isValidObjectVersion(targetIdVer)) { timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, targetIdVer, resultIds, params, lwm2mClientRpcRequest); if (request != null) { try { - this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); + this.sendRequest(lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); } catch (ClientSleepingException e) { SimpleDownlinkRequest finalRequest = request; long finalTimeoutInMs = timeoutInMs; LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; - lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); + lwM2MClient.getQueuedRequests().add(() -> sendRequest(lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); } catch (Exception e) { log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); } @@ -222,8 +309,8 @@ public class LwM2mTransportRequest { } private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, - ContentFormat contentFormat, String target, String targetIdVer, - LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { + ContentFormat contentFormat, String target, String targetIdVer, + LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request = null; switch (typeOper) { case READ: @@ -320,15 +407,18 @@ public class LwM2mTransportRequest { return request; } - /** - * @param registration - - * @param request - - * @param timeoutInMs - - */ + private void sendRequest(LwM2mClient lwM2MClient, SimpleDownlinkRequest request, long timeoutInMs) { + try { + sendRequest(lwM2MClient, request, timeoutInMs, null); + } catch (ClientSleepingException e) { + //TODO: this may cause infinite loop / memory leak. + lwM2MClient.getQueuedRequests().add(() -> sendRequest(lwM2MClient, request, timeoutInMs, null)); + } + } @SuppressWarnings({"error sendRequest"}) - private void sendRequest(Registration registration, LwM2mClient lwM2MClient, SimpleDownlinkRequest request, - long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { + private void sendRequest(LwM2mClient lwM2MClient, SimpleDownlinkRequest request, long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { + Registration registration = lwM2MClient.getRegistration(); context.getServer().send(registration, request, timeoutInMs, (ResponseCallback) response -> { if (!lwM2MClient.isInit()) { @@ -455,8 +545,8 @@ public class LwM2mTransportRequest { /** * processing a response from a client * - * @param path - - * @param response - + * @param path - + * @param response - */ private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response, SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { @@ -550,8 +640,7 @@ public class LwM2mTransportRequest { if (rpcRequest != null) { rpcRequest.setInfoMsg(msg); } - } - else if (rpcRequest != null) { + } else if (rpcRequest != null) { handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); } } @@ -610,4 +699,5 @@ public class LwM2mTransportRequest { handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); } } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 6159a201b4..dd77209e24 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -16,13 +16,9 @@ package org.thingsboard.server.transport.lwm2m.server; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; -import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; -import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.attributes.Attribute; @@ -42,17 +38,17 @@ import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.registration.Registration; import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -60,7 +56,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; @@ -379,7 +374,7 @@ public class LwM2mTransportUtil { switch (updateResultFw) { case INITIAL: return equalsFwSateToFirmwareUpdateStatus(stateFw); - case UPDATE_SUCCESSFULLY: + case UPDATE_SUCCESSFULLY: return UPDATED; case NOT_ENOUGH: case OUT_OFF_MEMORY: @@ -399,7 +394,7 @@ public class LwM2mTransportUtil { switch (updateResultFw) { case INITIAL: return VERIFIED; - case UPDATE_SUCCESSFULLY: + case UPDATE_SUCCESSFULLY: return UPDATED; case NOT_ENOUGH: case OUT_OFF_MEMORY: @@ -695,26 +690,23 @@ public class LwM2mTransportUtil { } } - public static LwM2mOtaConvert convertOtaUpdateValueToString (String pathIdVer, Object value, ResourceModel.Type currentType) { - String path = convertPathFromIdVerToObjectId(pathIdVer); + public static LwM2mOtaConvert convertOtaUpdateValueToString(String pathIdVer, Object value, ResourceModel.Type currentType) { + String path = fromVersionedIdToObjectId(pathIdVer); LwM2mOtaConvert lwM2mOtaConvert = new LwM2mOtaConvert(); if (path != null) { if (FW_STATE_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); lwM2mOtaConvert.setValue(StateFw.fromStateFwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; - } - else if (FW_RESULT_ID.equals(path)) { + } else if (FW_RESULT_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; - } - else if (SW_UPDATE_STATE_ID.equals(path)) { + } else if (SW_UPDATE_STATE_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; - } - else if (SW_RESULT_ID.equals(path)) { + } else if (SW_RESULT_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; @@ -738,113 +730,32 @@ public class LwM2mTransportUtil { return null; } - - public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { - LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); - lwM2MClientProfile.setTenantId(tenantId); - lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject()); - lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject()); - lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); - lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); - lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray()); - lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); - return lwM2MClientProfile; - } - - /** - * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard - * Example: - * property: {"clientLwM2mSettings": { - * clientUpdateValueAfterConnect: false; - * } - * property: "observeAttr" - * {"keyName": { - * "/3/0/1": "modelNumber", - * "/3/0/0": "manufacturer", - * "/3/0/2": "serialNumber" - * }, - * "attribute":["/2/0/1","/3/0/9"], - * "telemetry":["/1/0/1","/2/0/1","/6/0/1"], - * "observe":["/2/0","/2/0/0","/4/0/2"]} - * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, - * "/3_1.0/0": {"gt": 17}, - * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} - */ - //TODO: refactor this to valid jackson code instead of parsing manually. - public static LwM2mClientProfile toLwM2MClientProfile(DeviceProfile deviceProfile) { -// if (((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { -// Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); -// try { -// ObjectMapper mapper = new ObjectMapper(); -// String profileStr = mapper.writeValueAsString(profile); -// JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; -// return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; -// } catch (IOException e) { -// log.error("", e); -// } -// } - return null; - } - - public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { -// if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { -// Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); -// try { -// ObjectMapper mapper = new ObjectMapper(); -// String bootstrapStr = mapper.writeValueAsString(bootstrap); -// JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; -// return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; -// } catch (IOException e) { -// log.error("", e); -// } -// } - return null; - } - - private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) { - return (objectMsg != null && - !objectMsg.isJsonNull() && - objectMsg.has(CLIENT_LWM2M_SETTINGS) && - !objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonNull() && - objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonObject() && - objectMsg.has(OBSERVE_ATTRIBUTE_TELEMETRY) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonObject() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(KEY_NAME) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonObject() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonArray() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(TELEMETRY) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonArray() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(OBSERVE_LWM2M) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).isJsonArray() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE_LWM2M) && - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonNull() && - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonObject()); +// public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { +// LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); +// lwM2MClientProfile.setTenantId(tenantId); +// lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject()); +// lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject()); +// lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); +// lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); +// lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray()); +// lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); +// return lwM2MClientProfile; +// } + + public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) { + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) { + return (Lwm2mDeviceProfileTransportConfiguration) transportConfiguration; + } else { + log.warn("[{}] Received profile with invalid transport configuration: {}", deviceProfile.getId(), deviceProfile.getProfileData().getTransportConfiguration()); + throw new IllegalArgumentException("Received profile with invalid transport configuration: " + transportConfiguration.getType()); + } } - private static boolean getValidateBootstrapProfileFromThingsboard(JsonObject objectMsg) { - return (objectMsg != null && - !objectMsg.isJsonNull() && - objectMsg.has(BOOTSTRAP) && - objectMsg.get(BOOTSTRAP).isJsonObject() && - !objectMsg.get(BOOTSTRAP).isJsonNull() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(SERVERS) && - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(SERVERS).isJsonNull() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(SERVERS).isJsonObject() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(BOOTSTRAP_SERVER) && - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(BOOTSTRAP_SERVER).isJsonNull() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(BOOTSTRAP_SERVER).isJsonObject() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(LWM2M_SERVER) && - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(LWM2M_SERVER).isJsonNull() && - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(LWM2M_SERVER).isJsonObject()); + public static BootstrapConfiguration getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { + return toLwM2MClientProfile(deviceProfile).getBootstrap(); } - public static JsonObject validateJson(String jsonStr) { JsonObject object = null; if (jsonStr != null && !jsonStr.isEmpty()) { @@ -901,8 +812,11 @@ public class LwM2mTransportUtil { }; } - public static String convertPathFromIdVerToObjectId(String pathIdVer) { + public static String fromVersionedIdToObjectId(String pathIdVer) { try { + if(pathIdVer == null) { + return null; + } String[] keyArray = pathIdVer.split(LWM2M_SEPARATOR_PATH); if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) { keyArray[1] = keyArray[1].split(LWM2M_SEPARATOR_KEY)[0]; @@ -911,7 +825,8 @@ public class LwM2mTransportUtil { return pathIdVer; } } catch (Exception e) { - return null; + log.warn("Issue converting path with version [{}] to path without version: ", pathIdVer, e); + throw new RuntimeException(e); } } @@ -1025,12 +940,6 @@ public class LwM2mTransportUtil { return attributeLists.toArray(Attribute[]::new); } - public static Set convertJsonArrayToSet(JsonArray jsonArray) { - List attributeListOld = new Gson().fromJson(jsonArray, new TypeToken>() { - }.getType()); - return Sets.newConcurrentHashSet(attributeListOld); - } - public static ResourceModel.Type equalsResourceTypeGetSimpleName(Object value) { switch (value.getClass().getSimpleName()) { case "Double": diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java index 2d981e0d4e..650726604f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java @@ -72,7 +72,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { public DynamicModel(Registration registration) { this.registration = registration; - this.tenantId = lwM2mClientContext.getProfile(registration).getTenantId(); + this.tenantId = lwM2mClientContext.getClientByEndpoint(registration.getEndpoint()).getTenantId(); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 519f32b076..a6586da0cc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -32,6 +32,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; @@ -58,7 +59,7 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; @@ -92,6 +93,8 @@ public class LwM2mClient implements Cloneable { @Getter private SecurityInfo securityInfo; @Getter + private TenantId tenantId; + @Getter private UUID deviceId; @Getter private SessionInfoProto session; @@ -127,19 +130,17 @@ public class LwM2mClient implements Cloneable { this.state = LwM2MClientState.CREATED; } - public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) { + public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID sessionId) { this.identity = identity; this.securityInfo = securityInfo; this.credentials = credentials; - this.profileId = profileId; this.init = false; - if (this.credentials != null && this.credentials.hasDeviceInfo()) { - this.session = createSession(nodeId, sessionId, credentials); - this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); - this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB()); - this.deviceName = session.getDeviceName(); - this.deviceProfileName = session.getDeviceType(); - } + this.session = createSession(nodeId, sessionId, credentials); + this.tenantId = new TenantId(new UUID(session.getTenantIdMSB(), session.getTenantIdLSB())); + this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); + this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB()); + this.deviceName = session.getDeviceName(); + this.deviceProfileName = session.getDeviceType(); } public void lock() { @@ -198,7 +199,7 @@ public class LwM2mClient implements Cloneable { this.resources.get(pathRezIdVer).setLwM2mResource(rez); return true; } else { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); if (resourceModel != null) { this.resources.put(pathRezIdVer, new ResourceValue(rez, resourceModel)); @@ -226,7 +227,7 @@ public class LwM2mClient implements Cloneable { } public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathObjectInstanceIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathObjectInstanceIdVer)); if (pathIds.isObjectInstance()) { Set rezIds = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources.entrySet() @@ -240,7 +241,7 @@ public class LwM2mClient implements Cloneable { } public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verRez = getVerFromPathIdVerOrId(pathIdVer); return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) @@ -248,7 +249,7 @@ public class LwM2mClient implements Cloneable { } public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verRez = getVerFromPathIdVerOrId(pathIdVer); return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) @@ -290,13 +291,13 @@ public class LwM2mClient implements Cloneable { } else { return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(), converter.convertValue(lwM2mResource.getValue(), - OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name()); + OPAQUE, STRING, new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))), lwM2mResource.getType().name()); } } public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, LwM2mValueConverterImpl converter) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; @@ -312,7 +313,7 @@ public class LwM2mClient implements Cloneable { public Collection getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, LwM2mValueConverterImpl converter) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; @@ -328,7 +329,7 @@ public class LwM2mClient implements Cloneable { } public boolean isValidObjectVersion(String path) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(path)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verRez = getVerFromPathIdVerOrId(path); return verRez == null ? TRANSPORT_DEFAULT_LWM2M_VERSION.equals(verSupportedObject) : verRez.equals(verSupportedObject); @@ -341,7 +342,7 @@ public class LwM2mClient implements Cloneable { public void deleteResources(String pathIdVer, LwM2mModelProvider modelProvider) { Set key = getKeysEqualsIdVer(pathIdVer); key.forEach(pathRez -> { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); if (resourceModel != null) { this.resources.get(pathRez).setResourceModel(resourceModel); @@ -361,7 +362,7 @@ public class LwM2mClient implements Cloneable { } private void saveResourceModel(String pathRez, LwM2mModelProvider modelProvider) { - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); this.resources.get(pathRez).setResourceModel(resourceModel); } @@ -395,16 +396,16 @@ public class LwM2mClient implements Cloneable { public LwM2mFwSwUpdate getFwUpdate (LwM2mClientContext clientContext) { if (this.fwUpdate == null) { - LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); - this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, lwM2mClientProfile.getFwUpdateStrategy()); + var profile = clientContext.getProfile(this.getProfileId()); + this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); } return this.fwUpdate; } public LwM2mFwSwUpdate getSwUpdate (LwM2mClientContext clientContext) { if (this.swUpdate == null) { - LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); - this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, lwM2mClientProfile.getSwUpdateStrategy()); + var profile = clientContext.getProfile(this.getProfileId()); + this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); } return this.fwUpdate; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index 613a8b7971..f0539a0ccc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -17,11 +17,12 @@ package org.thingsboard.server.transport.lwm2m.server.client; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Collection; -import java.util.Map; import java.util.Set; import java.util.UUID; @@ -41,15 +42,12 @@ public interface LwM2mClientContext { Collection getLwM2mClients(); - Map getProfiles(); + //TODO: replace UUID with DeviceProfileId + Lwm2mDeviceProfileTransportConfiguration getProfile(UUID profileUuId); - LwM2mClientProfile getProfile(UUID profileUuId); + Lwm2mDeviceProfileTransportConfiguration getProfile(Registration registration); - LwM2mClientProfile getProfile(Registration registration); - - Map setProfiles(Map profiles); - - LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile); + Lwm2mDeviceProfileTransportConfiguration profileUpdate(DeviceProfile deviceProfile); Set getSupportedIdVerInClient(LwM2mClient registration); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index c088ac8477..736722ddc4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -21,6 +21,7 @@ import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; @@ -50,7 +51,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { private final TbEditableSecurityStore securityStore; private final Map lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); - private Map profiles = new ConcurrentHashMap<>(); + private final Map profiles = new ConcurrentHashMap<>(); @Override public LwM2mClient getClientByEndpoint(String endpoint) { @@ -67,11 +68,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); if (securityInfo.getSecurityMode() != null) { if (securityInfo.getDeviceProfile() != null) { - UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile()) != null ? securityInfo.getDeviceProfile().getUuidId() : null; + profileUpdate(securityInfo.getDeviceProfile()); if (securityInfo.getSecurityInfo() != null) { - lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), profileUuid, UUID.randomUUID()); + lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), UUID.randomUUID()); } else if (NO_SEC.equals(securityInfo.getSecurityMode())) { - lwM2MClient.init(null, null, securityInfo.getMsg(), profileUuid, UUID.randomUUID()); + lwM2MClient.init(null, null, securityInfo.getMsg(), UUID.randomUUID()); } else { throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint())); } @@ -157,7 +158,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) { LwM2mClient client = getClientByEndpoint(registration.getEndpoint()); - client.init(null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); + client.init(null, null, credentials, UUID.randomUUID()); lwM2mClientsByRegistrationId.put(registration.getId(), client); profileUpdate(credentials.getDeviceProfile()); } @@ -168,35 +169,20 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } @Override - public Map getProfiles() { - return profiles; - } - - @Override - public LwM2mClientProfile getProfile(UUID profileId) { + public Lwm2mDeviceProfileTransportConfiguration getProfile(UUID profileId) { return profiles.get(profileId); } @Override - public LwM2mClientProfile getProfile(Registration registration) { - return this.getProfiles().get(getClientByEndpoint(registration.getEndpoint()).getProfileId()); + public Lwm2mDeviceProfileTransportConfiguration getProfile(Registration registration) { + return profiles.get(getClientByEndpoint(registration.getEndpoint()).getProfileId()); } @Override - public Map setProfiles(Map profiles) { - return this.profiles = profiles; - } - - @Override - public LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile) { - LwM2mClientProfile lwM2MClientProfile = deviceProfile != null ? - LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile) : null; - if (lwM2MClientProfile != null) { - profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); - return lwM2MClientProfile; - } else { - return null; - } + public Lwm2mDeviceProfileTransportConfiguration profileUpdate(DeviceProfile deviceProfile) { + Lwm2mDeviceProfileTransportConfiguration lwM2MClientProfile = LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile); + profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); + return lwM2MClientProfile; } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java deleted file mode 100644 index 67edbf2747..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.server.client; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import lombok.Data; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MClientStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MSoftwareUpdateStrategy; - -@Data -public class LwM2mClientProfile { - private final String clientStrategyStr = "clientStrategy"; - private final String fwUpdateStrategyStr = "fwUpdateStrategy"; - private final String swUpdateStrategyStr = "swUpdateStrategy"; - - private TenantId tenantId; - /** - * "clientLwM2mSettings": { - * "fwUpdateStrategy": "1", - * "swUpdateStrategy": "1", - * "clientStrategy": "1" - * } - **/ - private JsonObject postClientLwM2mSettings; - - /** - * {"keyName": { - * "/3_1.0/0/1": "modelNumber", - * "/3_1.0/0/0": "manufacturer", - * "/3_1.0/0/2": "serialNumber" - * } - **/ - private JsonObject postKeyNameProfile; - - /** - * [ "/3_1.0/0/0", "/3_1.0/0/1"] - */ - private JsonArray postAttributeProfile; - - /** - * [ "/3_1.0/0/0", "/3_1.0/0/2"] - */ - private JsonArray postTelemetryProfile; - - /** - * [ "/3_1.0/0", "/3_1.0/0/1, "/3_1.0/0/2"] - */ - private JsonArray postObserveProfile; - - /** - * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, - * "/3_1.0/0": {"gt": 17}, - * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} - */ - private JsonObject postAttributeLwm2mProfile; - - public LwM2mClientProfile clone() { - LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile(); - lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class); - lwM2mClientProfile.postKeyNameProfile = this.deepCopy(this.postKeyNameProfile, JsonObject.class); - lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class); - lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class); - lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class); - lwM2mClientProfile.postAttributeLwm2mProfile = this.deepCopy(this.postAttributeLwm2mProfile, JsonObject.class); - return lwM2mClientProfile; - } - - - private T deepCopy(T elements, Class type) { - try { - Gson gson = new Gson(); - return gson.fromJson(gson.toJson(elements), type); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public int getClientStrategy() { - return this.postClientLwM2mSettings.getAsJsonObject().has(this.clientStrategyStr) ? - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.clientStrategyStr).getAsString()) : - LwM2MClientStrategy.CLIENT_STRATEGY_1.code; - } - - public int getFwUpdateStrategy() { - return this.postClientLwM2mSettings.getAsJsonObject().has(this.fwUpdateStrategyStr) ? - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.fwUpdateStrategyStr).getAsString()) : - LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code; - } - - public int getSwUpdateStrategy() { - return this.postClientLwM2mSettings.getAsJsonObject().has(this.swUpdateStrategyStr) ? - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.swUpdateStrategyStr).getAsString()) : - LwM2MSoftwareUpdateStrategy.BINARY.code; - } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java index 53385b6f61..e11d50f29f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java @@ -52,7 +52,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer; @Slf4j @@ -152,8 +152,8 @@ public class LwM2mClientRpcRequest { else if (this.getTargetIdVer() != null && (EXECUTE == this.getTypeOper() || WRITE_REPLACE == this.getTypeOper()) - && !(new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResource() - || new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResourceInstance())) { + && !(new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.getTargetIdVer()))).isResource() + || new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.getTargetIdVer()))).isResourceInstance())) { this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY + ". Only Resource or ResourceInstance can be this operation"); } @@ -247,7 +247,7 @@ public class LwM2mClientRpcRequest { private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, DefaultLwM2MUplinkMsgHandler serviceImpl) { Map paramsIdVer = new ConcurrentHashMap<>(); - LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer))); + LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.targetIdVer))); if (targetId.isObjectInstance()) { params.forEach((k, v) -> { try { @@ -256,7 +256,7 @@ public class LwM2mClientRpcRequest { } catch (NumberFormatException e) { String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k); if (targetIdVer != null) { - LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(targetIdVer))); + LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(targetIdVer))); paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); } /** WRITE_UPDATE*/ diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultsAnalyzerParameters.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ParametersAnalyzeResult.java similarity index 92% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultsAnalyzerParameters.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ParametersAnalyzeResult.java index 45b52811d8..9cf304ab97 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultsAnalyzerParameters.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ParametersAnalyzeResult.java @@ -21,11 +21,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Data -public class ResultsAnalyzerParameters { +public class ParametersAnalyzeResult { Set pathPostParametersAdd; Set pathPostParametersDel; - public ResultsAnalyzerParameters() { + public ParametersAnalyzeResult() { this.pathPostParametersAdd = ConcurrentHashMap.newKeySet(); this.pathPostParametersDel = ConcurrentHashMap.newKeySet(); } From 898c937e5d4a1e753049d9d1b764ded6c4f1f660 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Thu, 10 Jun 2021 10:49:32 +0300 Subject: [PATCH 025/151] Removed redundant endpoint, RPK test is separated --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 3 +- ...Test.java => PskLwm2mIntegrationTest.java} | 20 ++-------- .../lwm2m/RpkLwM2MIntegrationTest.java | 39 +++++++++++++++++++ .../lwm2m/X509LwM2MIntegrationTest.java | 8 ++-- 4 files changed, 47 insertions(+), 23 deletions(-) rename application/src/test/java/org/thingsboard/server/transport/lwm2m/{PskRpkLwm2mIntegrationTest.java => PskLwm2mIntegrationTest.java} (61%) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/RpkLwM2MIntegrationTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 39b4d5ef2f..f223bb079b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -163,13 +163,12 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { protected static final int SECURE_PORT = 5686; protected static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT)); - protected static final String SECURE_ENDPOINT = "deviceAEndpoint"; + protected static final String ENDPOINT = "deviceAEndpoint"; protected static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT; protected static final int PORT = 5685; protected static final Security SECURITY = noSec("coap://localhost:" + PORT, 123); protected static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); - protected static final String ENDPOINT = "deviceAEndpoint"; public AbstractLwM2MIntegrationTest() { // create client credentials diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskLwm2mIntegrationTest.java similarity index 61% rename from application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/lwm2m/PskLwm2mIntegrationTest.java index 0d2f4d05a5..6d3e544d57 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskRpkLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/PskLwm2mIntegrationTest.java @@ -19,39 +19,25 @@ import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; import org.junit.Test; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials; -import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials; import java.nio.charset.StandardCharsets; import static org.eclipse.leshan.client.object.Security.psk; -import static org.eclipse.leshan.client.object.Security.rpk; -public class PskRpkLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { - @Test - public void testConnectWithRPKAndObserveTelemetry() throws Exception { - RPKClientCredentials rpkClientCredentials = new RPKClientCredentials(); - rpkClientCredentials.setEndpoint(SECURE_ENDPOINT); - rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded())); - Security security = rpk(SECURE_URI, - 123, - clientPublicKey.getEncoded(), - clientPrivateKey.getEncoded(), - serverX509Cert.getPublicKey().getEncoded()); - super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); - } +public class PskLwm2mIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testConnectWithPSKAndObserveTelemetry() throws Exception { String pskIdentity = "SOME_PSK_ID"; String pskKey = "73656372657450534b"; PSKClientCredentials clientCredentials = new PSKClientCredentials(); - clientCredentials.setEndpoint(SECURE_ENDPOINT); + clientCredentials.setEndpoint(ENDPOINT); clientCredentials.setKey(pskKey); clientCredentials.setIdentity(pskIdentity); Security security = psk(SECURE_URI, 123, pskIdentity.getBytes(StandardCharsets.UTF_8), Hex.decodeHex(pskKey.toCharArray())); - super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); + super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, ENDPOINT); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/RpkLwM2MIntegrationTest.java new file mode 100644 index 0000000000..eb81f91e2c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/RpkLwM2MIntegrationTest.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m; + +import org.eclipse.leshan.client.object.Security; +import org.eclipse.leshan.core.util.Hex; +import org.junit.Test; +import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials; + +import static org.eclipse.leshan.client.object.Security.rpk; + +public class RpkLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { + @Test + public void testConnectWithRPKAndObserveTelemetry() throws Exception { + RPKClientCredentials rpkClientCredentials = new RPKClientCredentials(); + rpkClientCredentials.setEndpoint(ENDPOINT); + rpkClientCredentials.setKey(Hex.encodeHexString(clientPublicKey.getEncoded())); + Security security = rpk(SECURE_URI, + 123, + clientPublicKey.getEncoded(), + clientPrivateKey.getEncoded(), + serverX509Cert.getPublicKey().getEncoded()); + super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, ENDPOINT); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java index 8e3c26298d..9158acbc6c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java @@ -26,25 +26,25 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @Test public void testConnectAndObserveTelemetry() throws Exception { X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(SECURE_ENDPOINT); + credentials.setEndpoint(ENDPOINT); Security security = x509(SECURE_URI, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); } @Test public void testConnectWithCertAndObserveTelemetry() throws Exception { X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(SECURE_ENDPOINT); + credentials.setEndpoint(ENDPOINT); credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted)); Security security = x509(SECURE_URI, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); - super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, SECURE_ENDPOINT); + super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT); } } From 32cd80957aebdc45700a5ddbfbc74e0de9c793c6 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 10 Jun 2021 13:19:17 +0300 Subject: [PATCH 026/151] Fix profile update --- .../server/DefaultLwM2MUplinkMsgHandler.java | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index 7653a2c099..5eeba5a109 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -39,6 +39,7 @@ import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageKey; @@ -936,18 +937,20 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { var oldProfile = clientContext.getProfile(deviceProfile.getUuidId()); if (clientContext.profileUpdate(deviceProfile) != null) { // #1 - Set attributeSetOld = oldProfile.getObserveAttr().getAttribute(); - Set telemetrySetOld = oldProfile.getObserveAttr().getTelemetry(); - Set observeOld = oldProfile.getObserveAttr().getObserve(); - Map keyNameOld = oldProfile.getObserveAttr().getKeyName(); - Map attributeLwm2mOld = oldProfile.getObserveAttr().getAttributeLwm2m(); + TelemetryMappingConfiguration oldTelemetryParams = oldProfile.getObserveAttr(); + Set attributeSetOld = oldTelemetryParams.getAttribute(); + Set telemetrySetOld = oldTelemetryParams.getTelemetry(); + Set observeOld = oldTelemetryParams.getObserve(); + Map keyNameOld = oldTelemetryParams.getKeyName(); + Map attributeLwm2mOld = oldTelemetryParams.getAttributeLwm2m(); var newProfile = clientContext.getProfile(deviceProfile.getUuidId()); - Set attributeSetNew = newProfile.getObserveAttr().getAttribute(); - Set telemetrySetNew = newProfile.getObserveAttr().getTelemetry(); - Set observeNew = newProfile.getObserveAttr().getObserve(); - Map keyNameNew = newProfile.getObserveAttr().getKeyName(); - Map attributeLwm2mNew = newProfile.getObserveAttr().getAttributeLwm2m(); + TelemetryMappingConfiguration newTelemetryParams = newProfile.getObserveAttr(); + Set attributeSetNew = newTelemetryParams.getAttribute(); + Set telemetrySetNew = newTelemetryParams.getTelemetry(); + Set observeNew = newTelemetryParams.getObserve(); + Map keyNameNew = newTelemetryParams.getKeyName(); + Map attributeLwm2mNew = newTelemetryParams.getAttributeLwm2m(); // #3 ParametersAnalyzeResult diff = new ParametersAnalyzeResult(); @@ -981,11 +984,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.readObserveFromProfile(client, diff.getPathPostParametersAdd(), READ); }); } - // #4.2 del - if (diff.getPathPostParametersDel().size() > 0) { - ParametersAnalyzeResult sendAttrToThingsboardDel = this.getAnalyzerParameters(diff.getPathPostParametersAdd(), diff.getPathPostParametersDel()); - diff.setPathPostParametersDel(sendAttrToThingsboardDel.getPathPostParametersDel()); - } // #5.1 if (!observeOld.equals(observeNew)) { From 05df5e03efbaff6a1a5b48d27dc6327962ecd4b2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 10 Jun 2021 11:26:33 +0300 Subject: [PATCH 027/151] Revert "js-executor: upgraded libraries to fix vulnerability warning (@google-cloud/pubsub", amqplib)" Untested version with breaking changes declared This reverts commit 80f83a86 --- msa/js-executor/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index ba940ee85a..187144da20 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -13,8 +13,8 @@ }, "dependencies": { "@azure/service-bus": "^1.1.9", - "@google-cloud/pubsub": "^2.12.0", - "amqplib": "^0.8.0", + "@google-cloud/pubsub": "^2.5.0", + "amqplib": "^0.6.0", "aws-sdk": "^2.741.0", "azure-sb": "^0.11.1", "config": "^3.3.1", From 3a31b9c5ea7451dce8e5936e196f9fdbbb75eb4c Mon Sep 17 00:00:00 2001 From: Kien Truong Date: Thu, 3 Jun 2021 11:05:02 +0700 Subject: [PATCH 028/151] Fix wrong configuration key for Compression Type Fix #4678 (cherry picked from commit c176ca94aa0f612c3bb3856ae2acf3a399cc74a4) --- msa/js-executor/queue/kafkaTemplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index b34727c977..7e19f373a8 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -26,7 +26,7 @@ const acks = Number(config.get('kafka.acks')); const maxBatchSize = Number(config.get('kafka.batch_size')); const linger = Number(config.get('kafka.linger_ms')); const requestTimeout = Number(config.get('kafka.requestTimeout')); -const compressionType = (config.get('kafka.requestTimeout') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; +const compressionType = (config.get('kafka.compression') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; let kafkaClient; let kafkaAdmin; From 9a4a94621a88dd7e6e329dc1d7767ff75e311e16 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 10 Jun 2021 14:31:51 +0300 Subject: [PATCH 029/151] js-executor: parameter added for Kafka PARTITIONS_CONSUMED_CONCURRENTLY to decrease max latency while scale down replicas --- msa/js-executor/config/custom-environment-variables.yml | 1 + msa/js-executor/config/default.yml | 1 + msa/js-executor/queue/kafkaTemplate.js | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 7773fdaf0c..34903c4541 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -29,6 +29,7 @@ kafka: acks: "TB_KAFKA_ACKS" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge batch_size: "${TB_KAFKA_BATCH_SIZE:128}" # for producer linger_ms: "${TB_KAFKA_LINGER_MS:1}" # for producer + partitions_consumed_concurrently: "${PARTITIONS_CONSUMED_CONCURRENTLY:1}" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency requestTimeout: "TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS" compression: "TB_QUEUE_KAFKA_COMPRESSION" # gzip or uncompressed topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index bcefc1c639..190cf097ed 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -29,6 +29,7 @@ kafka: acks: "1" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge batch_size: "128" # for producer linger_ms: "1" # for producer + partitions_consumed_concurrently: "1" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency requestTimeout: "30000" # The default value in kafkajs is: 30000 compression: "gzip" # gzip or uncompressed topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1" diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 7e19f373a8..46ded135e6 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -27,6 +27,7 @@ const maxBatchSize = Number(config.get('kafka.batch_size')); const linger = Number(config.get('kafka.linger_ms')); const requestTimeout = Number(config.get('kafka.requestTimeout')); const compressionType = (config.get('kafka.compression') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; +const partitionsConsumedConcurrently = Number(config.get('kafka.partitions_consumed_concurrently')); let kafkaClient; let kafkaAdmin; @@ -197,7 +198,7 @@ async function sendMessagesAsBatch(isImmediately) { logger.info('Started ThingsBoard JavaScript Executor Microservice.'); await consumer.run({ - //partitionsConsumedConcurrently: 1, // Default: 1 + partitionsConsumedConcurrently: partitionsConsumedConcurrently, eachMessage: async ({topic, partition, message}) => { let headers = message.headers; let key = message.key; From 9ffa50f4a528d3f7b99e167e3bd797213ed26314 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 10 Jun 2021 16:05:50 +0300 Subject: [PATCH 030/151] js-executor: fixed env variables mapping --- msa/js-executor/config/custom-environment-variables.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 34903c4541..4185e65835 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -27,9 +27,9 @@ kafka: servers: "TB_KAFKA_SERVERS" replication_factor: "TB_QUEUE_KAFKA_REPLICATION_FACTOR" acks: "TB_KAFKA_ACKS" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge - batch_size: "${TB_KAFKA_BATCH_SIZE:128}" # for producer - linger_ms: "${TB_KAFKA_LINGER_MS:1}" # for producer - partitions_consumed_concurrently: "${PARTITIONS_CONSUMED_CONCURRENTLY:1}" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency + batch_size: "TB_KAFKA_BATCH_SIZE" # for producer + linger_ms: "TB_KAFKA_LINGER_MS" # for producer + partitions_consumed_concurrently: "TB_KAFKA_PARTITIONS_CONSUMED_CONCURRENTLY" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency requestTimeout: "TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS" compression: "TB_QUEUE_KAFKA_COMPRESSION" # gzip or uncompressed topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" From f7ab3a21b2abbee796dd9291df290955284780ed Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 10 Jun 2021 16:11:48 +0300 Subject: [PATCH 031/151] LwM2M Refactoring --- .../server/DefaultLwM2MUplinkMsgHandler.java | 151 ++++++------------ ...va => DefaultLwM2mDownlinkMsgHandler.java} | 2 +- .../lwm2m/server/LwM2mDownlinkMsgHandler.java | 4 + .../lwm2m/server/client/LwM2mFwSwUpdate.java | 14 +- 4 files changed, 61 insertions(+), 110 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{LwM2mTransportRequest.java => DefaultLwM2mDownlinkMsgHandler.java} (99%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index 5eeba5a109..e6dc83bc44 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -132,20 +132,20 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; public final LwM2mClientContext clientContext; - public final LwM2mTransportRequest lwM2mTransportRequest; + public final DefaultLwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; private final Map rpcSubscriptions; public final Map firmwareUpdateState; public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, - @Lazy LwM2mTransportRequest lwM2mTransportRequest, + @Lazy DefaultLwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.config = config; this.helper = helper; this.clientContext = clientContext; - this.lwM2mTransportRequest = lwM2mTransportRequest; + this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; this.otaPackageDataCache = otaPackageDataCache; this.context = context; this.adaptor = adaptor; @@ -291,7 +291,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (registration != null) { LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { - this.lwM2mTransportRequest.sendAllRequest(client, null, OBSERVE_CANCEL_ALL, + this.defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, null, OBSERVE_CANCEL_ALL, null, null, this.config.getTimeout(), null); } } @@ -467,7 +467,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); } else { - lwM2mTransportRequest.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), + defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), null, lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), this.config.getTimeout(), lwm2mClientRpcRequest); @@ -595,7 +595,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { // #2 lwM2MClient.getPendingReadRequests().addAll(supportedObjects); - supportedObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ, + supportedObjects.forEach(path -> defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, path, READ, null, this.config.getTimeout(), null)); } // #1 @@ -612,7 +612,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds.addAll(profile.getObserveAttr().getTelemetry()); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> lwM2mTransportRequest.sendReadRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, targetId, this.config.getTimeout())); } private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { @@ -620,7 +620,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> lwM2mTransportRequest.sendObserveRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, targetId, this.config.getTimeout())); } private void sendWriteAttributeRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { @@ -628,7 +628,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { attributesMap = attributesMap.entrySet().stream().filter(target -> isSupportedTargetId(supportedObjects, target.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - attributesMap.forEach((targetId, params) -> lwM2mTransportRequest.sendWriteAttributesRequest(lwM2MClient, targetId, params, this.config.getTimeout())); + attributesMap.forEach((targetId, params) -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, targetId, params, this.config.getTimeout())); } @@ -637,7 +637,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> lwM2mTransportRequest.sendDiscoverRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, targetId, this.config.getTimeout())); } private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set supportedObjectIds, Set desiredObjectIds, ConcurrentHashMap params) { @@ -649,7 +649,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getPendingReadRequests().addAll(targetObjectIds); targetObjectIds.forEach(target -> { Object additionalParams = params != null ? params.get(target) : null; - lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); + defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); }); if (OBSERVE.equals(operationType)) { lwM2MClient.initReadValue(this, null); @@ -702,17 +702,17 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * set setClient_fw_info... = value **/ if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); + lwM2MClient.getFwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); } if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); + lwM2MClient.getSwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); } if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); - fwUpdate.updateStateOta(this, lwM2mTransportRequest, registration, path, ((Long) lwM2mResource.getValue()).intValue()); + fwUpdate.updateStateOta(this, defaultLwM2MDownlinkMsgHandler, registration, path, ((Long) lwM2mResource.getValue()).intValue()); } this.updateAttrTelemetry(registration, Collections.singleton(path)); } else { @@ -907,31 +907,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } - /** - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName) - * #2 Update in lwM2MClient: ...Profile if changes from update device - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName) - * #3.1 Attribute isChange (add&del) - * #3.2 Telemetry isChange (add&del) - * #3.3 KeyName isChange (add) - * #3.4 attributeLwm2m isChange (update WrightAttribute: add/update/del) - * #4 update - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard - * #4.2 del - * -- if add attributes includes del telemetry - result del for observe - * #5 - * #5.1 Observe isChange (add&del) - * #5.2 Observe.add - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client - * #5.3 Observe.del - * -- different between newObserve and oldObserve: send Request cancel observe to client - * #6 - * #6.1 - update WriteAttribute - * #6.2 - del WriteAttribute - * - * @param clients - - * @param deviceProfile - - */ //TODO: review and optimize the logic to minimize number of the requests to device. private void onDeviceProfileUpdate(List clients, DeviceProfile deviceProfile) { var oldProfile = clientContext.getProfile(deviceProfile.getUuidId()); @@ -952,82 +927,54 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Map keyNameNew = newTelemetryParams.getKeyName(); Map attributeLwm2mNew = newTelemetryParams.getAttributeLwm2m(); - // #3 - ParametersAnalyzeResult diff = new ParametersAnalyzeResult(); + Set observeToAdd = diffSets(observeOld, observeNew); + Set observeToRemove = diffSets(observeNew, observeOld); + + Set newObjectsToRead = new HashSet<>(); + // #3.1 if (!attributeSetOld.equals(attributeSetNew)) { - ParametersAnalyzeResult postAttributeAnalyzer = this.getAnalyzerParameters(attributeSetOld, attributeSetNew); - diff.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); - diff.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); + newObjectsToRead.addAll(diffSets(attributeSetOld, attributeSetNew)); } // #3.2 if (!telemetrySetOld.equals(telemetrySetNew)) { - ParametersAnalyzeResult postTelemetryAnalyzer = this.getAnalyzerParameters(telemetrySetOld, telemetrySetNew); - diff.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); - diff.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); + newObjectsToRead.addAll(diffSets(telemetrySetOld, telemetrySetNew)); } // #3.3 if (!keyNameOld.equals(keyNameNew)) { ParametersAnalyzeResult keyNameChange = this.getAnalyzerKeyName(keyNameOld, keyNameNew); - diff.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); + newObjectsToRead.addAll(keyNameChange.getPathPostParametersAdd()); } // #3.4, #6 if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { - this.getAnalyzerAttributeLwm2m(clients, attributeLwm2mOld, attributeLwm2mNew); + this.compareAndSendWriteAttributes(clients, attributeLwm2mOld, attributeLwm2mNew); } // #4.1 add - if (diff.getPathPostParametersAdd().size() > 0) { + if (!newObjectsToRead.isEmpty()) { + Set newObjectsToReadButNotNewInObserve = diffSets(observeToAdd, newObjectsToRead); // update value in Resources - clients.forEach(client -> { - this.readObserveFromProfile(client, diff.getPathPostParametersAdd(), READ); - }); + if(!newObjectsToReadButNotNewInObserve.isEmpty()) { + clients.forEach(client -> this.readObserveFromProfile(client, newObjectsToReadButNotNewInObserve, READ)); + } } - // #5.1 - if (!observeOld.equals(observeNew)) { - //#5.2 add - // path Attr/Telemetry includes newObserve - attributeSetOld.addAll(telemetrySetOld); - ParametersAnalyzeResult sendObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeOld); // add observe - attributeSetNew.addAll(telemetrySetNew); - ParametersAnalyzeResult sendObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeNew); // add observe - // does not include oldObserve - ParametersAnalyzeResult postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); - // send Request observe to Client - clients.forEach(client -> { - Registration registration = client.getRegistration(); - if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { - this.readObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); - } - // 5.3 del - // send Request cancel observe to Client - if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { - this.cancelObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersDel()); - } - }); + // Calculating difference between old and new flags. + if(!observeToAdd.isEmpty()){ + clients.forEach(client -> this.readObserveFromProfile(client, observeToAdd, OBSERVE)); + } + if(!observeToRemove.isEmpty()){ + clients.forEach(client -> this.cancelObserveFromProfile(client, observeToRemove)); } } } /** - * Compare old list with new list after change AttrTelemetryObserve in config Profile - * - * @param parametersOld - - * @param parametersNew - - * @return ResultsAnalyzerParameters: add && new + Returns new set with elements that are present in set B(new) but absent in set A(old). */ - private ParametersAnalyzeResult getAnalyzerParameters(Set parametersOld, Set parametersNew) { - ParametersAnalyzeResult analyzerParameters = null; - if (!parametersOld.equals(parametersNew)) { - analyzerParameters = new ParametersAnalyzeResult(); - analyzerParameters.setPathPostParametersAdd(parametersNew - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet())); - analyzerParameters.setPathPostParametersDel(parametersOld - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet())); - } - return analyzerParameters; + private static Set diffSets(Set a, Set b){ + return b.stream().filter(p -> !a.contains(p)).collect(Collectors.toSet()); } private ParametersAnalyzeResult getAnalyzerParametersIn(Set parametersObserve, Set parameters) { @@ -1048,10 +995,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(target)); if (pathIds.isResource()) { if (READ.equals(typeOper)) { - lwM2mTransportRequest.sendAllRequest(client, target, typeOper, + defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, target, typeOper, null, this.config.getTimeout(), null); } else if (OBSERVE.equals(typeOper)) { - lwM2mTransportRequest.sendAllRequest(client, target, typeOper, + defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, target, typeOper, null, this.config.getTimeout(), null); } } @@ -1073,7 +1020,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * #6.2 - send empty WriteAttribute * */ - private void getAnalyzerAttributeLwm2m(List clients, Map lwm2mAttributesOld, Map lwm2mAttributesNew) { + private void compareAndSendWriteAttributes(List clients, Map lwm2mAttributesOld, Map lwm2mAttributesNew) { ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); Set pathOld = lwm2mAttributesOld.keySet(); Set pathNew = lwm2mAttributesNew.keySet(); @@ -1094,7 +1041,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - pathSend.forEach(target -> lwM2mTransportRequest.sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target), this.config.getTimeout())); + pathSend.forEach(target -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target), this.config.getTimeout())); } }); } @@ -1105,7 +1052,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - pathSend.forEach(target -> lwM2mTransportRequest.sendWriteAttributesRequest(client, target, new ObjectAttributes(), this.config.getTimeout())); + pathSend.forEach(target -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(client, target, new ObjectAttributes(), this.config.getTimeout())); } }); } @@ -1114,7 +1061,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set paramAnallyzer) { paramAnallyzer.forEach(pathIdVer -> { if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) { - lwM2mTransportRequest.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null); + defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null); } } ); @@ -1122,7 +1069,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { - lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null); + defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null); } else { log.error("Failed update resource [{}] [{}]", path, valueNew); String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", @@ -1293,9 +1240,9 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { fwUpdate.setCurrentTitle(response.getTitle()); fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); if (rpcRequest == null) { - fwUpdate.sendReadObserveInfo(lwM2mTransportRequest); + fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); } else { - fwUpdate.writeFwSwWare(handler, lwM2mTransportRequest); + fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); } } else { String msgError = String.format("OtaPackage device: %s, version: %s, stateUpdate: %s", @@ -1336,11 +1283,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); if (rpcRequest == null) { - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); } else { - lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); } } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java similarity index 99% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java index a1c4227728..30fee78d1a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java @@ -114,7 +114,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c @Service @TbLwM2mTransportComponent @RequiredArgsConstructor -public class LwM2mTransportRequest { +public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { private ExecutorService responseRequestExecutor; public LwM2mValueConverterImpl converter; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java new file mode 100644 index 0000000000..0beb8ea7cb --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java @@ -0,0 +1,4 @@ +package org.thingsboard.server.transport.lwm2m.server; + +public interface LwM2mDownlinkMsgHandler { +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index a13253282e..56a601b3d2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest; +import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import java.util.ArrayList; @@ -156,7 +156,7 @@ public class LwM2mFwSwUpdate { } } - public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) { + public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request, String pathIdVer) { if (pathIdVer != null) { this.pendingInfoRequestsStart.remove(pathIdVer); } @@ -176,7 +176,7 @@ public class LwM2mFwSwUpdate { * Send FsSw to Lwm2mClient: * before operation Write: fw_state = DOWNLOADING */ - public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { + public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); @@ -230,7 +230,7 @@ public class LwM2mFwSwUpdate { * fw_state/sw_state = UPDATING * send execute */ - public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { + public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest); } @@ -381,7 +381,7 @@ public class LwM2mFwSwUpdate { return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; } - private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request) { + private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { request.sendAllRequest(lwM2MClient, convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, null, null, 0, null); @@ -401,7 +401,7 @@ public class LwM2mFwSwUpdate { } } - public void sendReadObserveInfo(LwM2mTransportRequest request) { + public void sendReadObserveInfo(DefaultLwM2mDownlinkMsgHandler request) { this.infoFwSwUpdate = true; this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pathStateId, this.lwM2MClient.getRegistration())); @@ -433,7 +433,7 @@ public class LwM2mFwSwUpdate { * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") * - finished operation Execute (FwUpdate) */ - public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mTransportRequest request, + public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request, Registration registration, String path, int value) { if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { From 7e3087e107f67b59597b4d94e18b218fa8fe3325 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 10 Jun 2021 19:40:13 +0300 Subject: [PATCH 032/151] First iteration of downlink handler refactoring --- .../server/DefaultLwM2MUplinkMsgHandler.java | 97 +---- .../DefaultLwM2mDownlinkMsgHandler.java | 393 ++++++++++++------ .../lwm2m/server/LwM2mDownlinkMsgHandler.java | 44 ++ .../server/LwM2mTransportServerHelper.java | 23 +- .../lwm2m/server/client/LwM2mClient.java | 6 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 38 +- .../downlink/DownlinkRequestCallback.java | 25 ++ .../lwm2m/utils/LwM2mValueConverterImpl.java | 2 +- 8 files changed, 369 insertions(+), 259 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index e6dc83bc44..45e07eaf4c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -132,13 +132,13 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; public final LwM2mClientContext clientContext; - public final DefaultLwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; + public final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; private final Map rpcSubscriptions; public final Map firmwareUpdateState; public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, - @Lazy DefaultLwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, + @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; @@ -291,8 +291,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (registration != null) { LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { - this.defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, null, OBSERVE_CANCEL_ALL, - null, null, this.config.getTimeout(), null); + defaultLwM2MDownlinkMsgHandler.sendCancelAllRequest(client, this.config.getTimeout(), null); } } } @@ -595,8 +594,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { // #2 lwM2MClient.getPendingReadRequests().addAll(supportedObjects); - supportedObjects.forEach(path -> defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, path, READ, - null, this.config.getTimeout(), null)); + supportedObjects.forEach(path -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, path, this.config.getTimeout())); } // #1 this.sendReadRequests(lwM2MClient, profile, supportedObjects); @@ -640,24 +638,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, targetId, this.config.getTimeout())); } - private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set supportedObjectIds, Set desiredObjectIds, ConcurrentHashMap params) { - if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) { - Set targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target) - ).collect(Collectors.toUnmodifiableSet()); - if (!targetObjectIds.isEmpty()) { - //TODO: remove this side effect? - lwM2MClient.getPendingReadRequests().addAll(targetObjectIds); - targetObjectIds.forEach(target -> { - Object additionalParams = params != null ? params.get(target) : null; - defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); - }); - if (OBSERVE.equals(operationType)) { - lwM2MClient.initReadValue(this, null); - } - } - } - } - /** * @param registration - * @param lwM2mObject - @@ -955,56 +935,32 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (!newObjectsToRead.isEmpty()) { Set newObjectsToReadButNotNewInObserve = diffSets(observeToAdd, newObjectsToRead); // update value in Resources - if(!newObjectsToReadButNotNewInObserve.isEmpty()) { - clients.forEach(client -> this.readObserveFromProfile(client, newObjectsToReadButNotNewInObserve, READ)); + for (String targetId : newObjectsToReadButNotNewInObserve) { + clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(client, targetId, this.config.getTimeout())); } } // Calculating difference between old and new flags. - if(!observeToAdd.isEmpty()){ - clients.forEach(client -> this.readObserveFromProfile(client, observeToAdd, OBSERVE)); + if (!observeToAdd.isEmpty()) { + for (String targetId : observeToAdd) { + clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendObserveRequest(client, targetId, this.config.getTimeout())); + } } - if(!observeToRemove.isEmpty()){ - clients.forEach(client -> this.cancelObserveFromProfile(client, observeToRemove)); + if (!observeToRemove.isEmpty()) { + for (String targetId : observeToRemove) { + clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, targetId, this.config.getTimeout(), null)); + } } } } /** - Returns new set with elements that are present in set B(new) but absent in set A(old). + Returns new set with elements that are present in set B(new) but absent in set A(old). */ - private static Set diffSets(Set a, Set b){ + private static Set diffSets(Set a, Set b) { return b.stream().filter(p -> !a.contains(p)).collect(Collectors.toSet()); } - private ParametersAnalyzeResult getAnalyzerParametersIn(Set parametersObserve, Set parameters) { - ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); - analyzerParameters.setPathPostParametersAdd(parametersObserve - .stream().filter(parameters::contains).collect(Collectors.toSet())); - return analyzerParameters; - } - - /** - * Update Resource value after change RezAttrTelemetry in config Profile - * send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() - * - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] - */ - private void readObserveFromProfile(LwM2mClient client, Set targets, LwM2mTypeOper typeOper) { - targets.forEach(target -> { - LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(target)); - if (pathIds.isResource()) { - if (READ.equals(typeOper)) { - defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, target, typeOper, - null, this.config.getTimeout(), null); - } else if (OBSERVE.equals(typeOper)) { - defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, target, typeOper, - null, this.config.getTimeout(), null); - } - } - }); - } - private ParametersAnalyzeResult getAnalyzerKeyName(Map keyNameOld, Map keyNameNew) { ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); Set paths = keyNameNew.entrySet() @@ -1058,24 +1014,15 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set paramAnallyzer) { - paramAnallyzer.forEach(pathIdVer -> { - if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) { - defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null); - } - } - ); - } - - private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { - if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { - defaultLwM2MDownlinkMsgHandler.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null); + private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String path) { + if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { + defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, path, newValue, this.config.getTimeout(), null); } else { - log.error("Failed update resource [{}] [{}]", path, valueNew); + log.error("Failed update resource [{}] [{}]", path, newValue); String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", - LOG_LW2M_ERROR, path, valueNew); + LOG_LW2M_ERROR, path, newValue); this.sendLogsToThingsboard(lwM2MClient, logMsg); - log.info("Failed update resource [{}] [{}]", path, valueNew); + log.info("Failed update resource [{}] [{}]", path, newValue); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java index 30fee78d1a..3ba3de2b75 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java @@ -30,6 +30,7 @@ import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; +import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.DeleteRequest; @@ -53,13 +54,13 @@ import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -79,16 +80,13 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; -import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; -import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; import static org.eclipse.leshan.core.attributes.Attribute.STEP; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; @@ -106,9 +104,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @Service @@ -131,26 +129,30 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); } - public void sendReadRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { - sendReadRequest(lwM2MClient, targetId, lwM2MClient.getDefaultContentFormat(), timeout); + @Override + public void sendReadRequest(LwM2mClient client, String targetId, Long timeout) { + sendReadRequest(client, targetId, client.getDefaultContentFormat(), timeout); } - public void sendReadRequest(LwM2mClient lwM2MClient, String targetId, ContentFormat contentFormat, Long timeout) { + @Override + public void sendReadRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout) { String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { - sendRequest(lwM2MClient, new ReadRequest(contentFormat, objectId), timeout); + if (objectId != null && client.isValidObjectVersion(targetId)) { + sendRequest(client, new ReadRequest(contentFormat, objectId), timeout); } } - public void sendObserveRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { - sendObserveRequest(lwM2MClient, targetId, lwM2MClient.getDefaultContentFormat(), timeout); + @Override + public void sendObserveRequest(LwM2mClient client, String targetId, Long timeout) { + sendObserveRequest(client, targetId, client.getDefaultContentFormat(), timeout); } - public void sendObserveRequest(LwM2mClient lwM2MClient, String targetId, ContentFormat contentFormat, Long timeout) { + @Override + public void sendObserveRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout) { String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { + if (objectId != null && client.isValidObjectVersion(targetId)) { LwM2mPath resultIds = new LwM2mPath(objectId); - Set observations = context.getServer().getObservationService().getObservations(lwM2MClient.getRegistration()); + Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { ObserveRequest request; if (resultIds.isResource()) { @@ -160,22 +162,68 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } else { request = new ObserveRequest(contentFormat, resultIds.getObjectId()); } - log.info("[{}] Send observation: {}.", lwM2MClient.getEndpoint(), targetId); - sendRequest(lwM2MClient, request, timeout); + log.info("[{}] Send observation: {}.", client.getEndpoint(), targetId); + sendRequest(client, request, timeout); } } } - public void sendDiscoverRequest(LwM2mClient lwM2MClient, String targetId, Long timeout) { + @Override + public void sendExecuteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { + sendExecuteRequest(client, targetId, null, timeout, callback); + } + + @Override + public void sendExecuteRequest(LwM2mClient client, String targetId, Object params, Long timeout, DownlinkRequestCallback callback) { + String target = fromVersionedIdToObjectId(targetId); + LwM2mPath resultIds = new LwM2mPath(target); + ResourceModel resourceModelExecute = client.getResourceModel(targetId, this.config.getModelProvider()); + if (resourceModelExecute != null) { + ExecuteRequest request; + if (params != null && !resourceModelExecute.multiple) { + request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds)); + } else { + request = new ExecuteRequest(target); + } + sendRequest(client, request, timeout); + } + } + + public void sendDeleteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { + String target = fromVersionedIdToObjectId(targetId); + sendRequest(client, new DeleteRequest(target), timeout); + } + + @Override + public void sendCancelObserveRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration(), fromVersionedIdToObjectId(targetId)); + String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), targetId, observeCancelCnt); + if (callback != null) { + callback.onSuccess(client, observeCancelMsg); + } + } + + @Override + public void sendCancelAllRequest(LwM2mClient client, Long timeout, DownlinkRequestCallback callback) { + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration()); + String observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), observeCancelCnt); + if (callback != null) { + callback.onSuccess(client, observeCancelMsg); + } + } + + @Override + public void sendDiscoverRequest(LwM2mClient client, String targetId, Long timeout) { String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && lwM2MClient.isValidObjectVersion(targetId)) { - sendRequest(lwM2MClient, new DiscoverRequest(objectId), timeout); + if (objectId != null && client.isValidObjectVersion(targetId)) { + sendRequest(client, new DiscoverRequest(objectId), timeout); } } - public void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params, Long timeout) { + @Override + public void sendWriteAttributesRequest(LwM2mClient client, String targetId, ObjectAttributes params, Long timeout) { String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && lwM2MClient.isValidObjectVersion(targetId) && params != null) { + if (objectId != null && client.isValidObjectVersion(targetId) && params != null) { List attributes = new LinkedList<>(); // Dimension and Object version are read only attributes. // addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); @@ -186,7 +234,55 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { addAttribute(attributes, LESSER_THAN, params.getLt()); addAttribute(attributes, STEP, params.getSt()); AttributeSet attributeSet = new AttributeSet(attributes); - sendRequest(lwM2MClient, new WriteAttributesRequest(objectId, attributeSet), timeout); + sendRequest(client, new WriteAttributesRequest(objectId, attributeSet), timeout); + } + } + + @Override + public void sendWriteReplaceRequest(LwM2mClient client, String targetIdVer, Object newValue, Long timeout, DownlinkRequestCallback callback) { + ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); + if (resourceModelWrite != null) { + ContentFormat contentFormat = convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); + try { + LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(targetIdVer)); + WriteRequest request = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat, + path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), newValue); + sendRequest(client, request, timeout); + } catch (Exception e) { + callback.onError(client, e.getMessage(), e); + } + } + } + + @Override + public void sendWriteUpdateRequest(LwM2mClient client, String targetIdVer, Object newValue, ContentFormat contentFormat, Long timeout, DownlinkRequestCallback callback) { + LwM2mPath resultIds = new LwM2mPath(fromVersionedIdToObjectId(targetIdVer)); + if (resultIds.isResource()) { + /* + * send request: path = '/3/0' node == wM2mObjectInstance + * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} + **/ + Collection resources = client.getNewResourceForInstance(targetIdVer, newValue, this.config.getModelProvider(), this.converter); + ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); + WriteRequest request = new WriteRequest(WriteRequest.Mode.UPDATE, convertResourceModelTypeToContentFormat(client, resourceModelWrite.type), resultIds.getObjectId(), + resultIds.getObjectInstanceId(), resources); + sendRequest(client, request, timeout); + } else if (resultIds.isObjectInstance()) { + /* + * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" + * int rscId = resultIds.getObjectInstanceId(); + * contentFormat – Format of the payload (TLV or JSON). + */ + Collection resources = client.getNewResourcesForInstance(targetIdVer, newValue, this.config.getModelProvider(), this.converter); + if (resources.size() > 0) { + contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : client.getDefaultContentFormat(); + WriteRequest request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); + sendRequest(client, request, timeout); + } else { + callback.onError(client, "No resources to update!", new IllegalArgumentException()); + } + } else { + callback.onError(client, "Update of the root level object is not supported yet", new IllegalArgumentException()); } } @@ -204,32 +300,49 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } } - public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { - sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest); + private static ContentFormat convertResourceModelTypeToContentFormat(LwM2mClient client, ResourceModel.Type type) { + switch (type) { + case BOOLEAN: + case STRING: + case TIME: + case INTEGER: + case FLOAT: + return client.getDefaultContentFormat(); + case OPAQUE: + return ContentFormat.OPAQUE; + case OBJLNK: + return ContentFormat.LINK; + default: + } + throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); + } + + public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { + sendAllRequest(client, targetIdVer, typeOper, client.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest); } - public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, + public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { - Registration registration = lwM2MClient.getRegistration(); + Registration registration = client.getRegistration(); try { String target = fromVersionedIdToObjectId(targetIdVer); if (contentFormat == null) { - contentFormat = ContentFormat.DEFAULT; + contentFormat = client.getDefaultContentFormat(); } LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0) { - if (lwM2MClient.isValidObjectVersion(targetIdVer)) { + if (client.isValidObjectVersion(targetIdVer)) { timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; - SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, + SimpleDownlinkRequest request = createRequest(registration, client, typeOper, contentFormat, target, targetIdVer, resultIds, params, lwm2mClientRpcRequest); if (request != null) { try { - this.sendRequest(lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); + this.sendRequest(client, request, timeoutInMs, lwm2mClientRpcRequest); } catch (ClientSleepingException e) { SimpleDownlinkRequest finalRequest = request; long finalTimeoutInMs = timeoutInMs; LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; - lwM2MClient.getQueuedRequests().add(() -> sendRequest(lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); + client.getQueuedRequests().add(() -> sendRequest(client, finalRequest, finalTimeoutInMs, finalRpcRequest)); } catch (Exception e) { log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); } @@ -246,7 +359,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); if (lwm2mClientRpcRequest != null) { - ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); + ResourceModel resourceModel = client.getResourceModel(targetIdVer, this.config.getModelProvider()); String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } @@ -270,7 +383,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, typeOper.name(), paths); - this.handler.sendLogsToThingsboard(lwM2MClient, msg); + this.handler.sendLogsToThingsboard(client, msg); if (lwm2mClientRpcRequest != null) { String valueMsg = String.format("Paths - %s", paths); handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); @@ -289,18 +402,18 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), observeCancelCnt); } - this.afterObserveCancel(lwM2MClient, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); + this.afterObserveCancel(client, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); break; // lwm2mClientRpcRequest != null case FW_UPDATE: - handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); + handler.getInfoFirmwareUpdate(client, lwm2mClientRpcRequest); break; } } } catch (Exception e) { String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, typeOper.name(), e.getMessage()); - handler.sendLogsToThingsboard(lwM2MClient, msg); + handler.sendLogsToThingsboard(client, msg); if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); @@ -308,7 +421,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } } - private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, + private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient client, LwM2mTypeOper typeOper, ContentFormat contentFormat, String target, String targetIdVer, LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { SimpleDownlinkRequest request = null; @@ -337,7 +450,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } break; case EXECUTE: - ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); + ResourceModel resourceModelExecute = client.getResourceModel(targetIdVer, this.config.getModelProvider()); if (resourceModelExecute != null) { if (params != null && !resourceModelExecute.multiple) { request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds)); @@ -353,12 +466,12 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { * contentFormat -> TLV, TLV, TLV, TLV, OPAQUE, TLV, LINK * JSON, TEXT; **/ - ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); + ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); if (resourceModelWrite != null) { - contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), - resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, - lwM2MClient, rpcRequest); +// contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); +// request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), +// resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, +// client, rpcRequest); } break; case WRITE_UPDATE: @@ -367,14 +480,14 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { * send request: path = '/3/0' node == wM2mObjectInstance * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} **/ - Collection resources = lwM2MClient.getNewResourceForInstance( + Collection resources = client.getNewResourceForInstance( targetIdVer, params, this.config.getModelProvider(), this.converter); - contentFormat = getContentFormatByResourceModelType(lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()), - contentFormat); - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), - resultIds.getObjectInstanceId(), resources); +// contentFormat = getContentFormatByResourceModelType(client.getResourceModel(targetIdVer, this.config.getModelProvider()), +// contentFormat); +// request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), +// resultIds.getObjectInstanceId(), resources); } /** * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" @@ -383,7 +496,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { */ else if (resultIds.isObjectInstance()) { if (((ConcurrentHashMap) params).size() > 0) { - Collection resources = lwM2MClient.getNewResourcesForInstance( + Collection resources = client.getNewResourcesForInstance( targetIdVer, params, this.config.getModelProvider(), this.converter); @@ -407,33 +520,33 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { return request; } - private void sendRequest(LwM2mClient lwM2MClient, SimpleDownlinkRequest request, long timeoutInMs) { + private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs) { try { - sendRequest(lwM2MClient, request, timeoutInMs, null); + sendRequest(client, request, timeoutInMs, null); } catch (ClientSleepingException e) { //TODO: this may cause infinite loop / memory leak. - lwM2MClient.getQueuedRequests().add(() -> sendRequest(lwM2MClient, request, timeoutInMs, null)); + client.getQueuedRequests().add(() -> sendRequest(client, request, timeoutInMs, null)); } } @SuppressWarnings({"error sendRequest"}) - private void sendRequest(LwM2mClient lwM2MClient, SimpleDownlinkRequest request, long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { - Registration registration = lwM2MClient.getRegistration(); + private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { + Registration registration = client.getRegistration(); context.getServer().send(registration, request, timeoutInMs, (ResponseCallback) response -> { - if (!lwM2MClient.isInit()) { - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + if (!client.isInit()) { + client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { - this.handleResponse(lwM2MClient, request.getPath().toString(), response, request, rpcRequest); + this.handleResponse(client, request.getPath().toString(), response, request, rpcRequest); } else { String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); - handler.sendLogsToThingsboard(lwM2MClient, msg); + handler.sendLogsToThingsboard(client, msg); log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); - if (!lwM2MClient.isInit()) { - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + if (!client.isInit()) { + client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } /** Not Found */ if (rpcRequest != null) { @@ -442,11 +555,11 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { /** Not Found set setClient_fw_info... = empty **/ - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); + if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { + client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); + if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { + client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); @@ -459,11 +572,11 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { /** version == null set setClient_fw_info... = empty **/ - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); + if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { + client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); + if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { + client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); @@ -471,12 +584,12 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { this.afterExecuteFwSwUpdateError(registration, request, e.getMessage()); } - if (!lwM2MClient.isInit()) { - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + if (!client.isInit()) { + client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); - handler.sendLogsToThingsboard(lwM2MClient, msg); + handler.sendLogsToThingsboard(client, msg); log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); if (rpcRequest != null) { handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); @@ -484,51 +597,51 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { }); } - private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, - Integer resourceId, Object value, ResourceModel.Type type, - LwM2mClient client, LwM2mClientRpcRequest rpcRequest) { - try { - if (type != null) { - switch (type) { - case STRING: // String - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, value.toString()) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString()); - case INTEGER: // Long - final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString())); - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueInt) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt); - case OBJLNK: // ObjectLink - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())); - case BOOLEAN: // Boolean - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())); - case FLOAT: // Double - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.parseDouble(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString())); - case TIME: // Date - Date date = new Date(Long.decode(value.toString())); - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); - case OPAQUE: // byte[] value, base64 - byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray()); - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) : - new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); - default: + private WriteRequest getWriteRequestSingleResource(ResourceModel.Type type, ContentFormat contentFormat, int objectId, int instanceId, int resourceId, Object value) { + switch (type) { + case STRING: // String + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString()); + case INTEGER: // Long + final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString())); + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt); + case OBJLNK: // ObjectLink + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())); + case BOOLEAN: // Boolean + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())); + case FLOAT: // Double + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString())); + case TIME: // Date + Date date = new Date(Long.decode(value.toString())); + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); + case OPAQUE: // byte[] value, base64 + byte[] valueRequest; + if (value instanceof byte[]) { + valueRequest = (byte[]) value; + } else { + valueRequest = Hex.decodeHex(value.toString().toCharArray()); } - } - if (rpcRequest != null) { - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; - String errorMsg = String.format("Bad ResourceModel Operations (E): Resource path - %s ResourceModel type - %s", patn, type); - rpcRequest.setErrorMsg(errorMsg); - } - return null; - } catch (NumberFormatException e) { - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; - String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", - patn, type, value, e.toString()); - handler.sendLogsToThingsboard(client, msg); - log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); - if (rpcRequest != null) { - String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); - handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); - } - return null; + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); + default: } +// TODO: throw exception and execute callback. +//// if (rpcRequest != null) { +//// String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; +//// String errorMsg = String.format("Bad ResourceModel Operations (E): Resource path - %s ResourceModel type - %s", patn, type); +//// rpcRequest.setErrorMsg(errorMsg); +//// } +// return null; +// } catch (NumberFormatException e) { +// String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; +// String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", +// patn, type, value, e.toString()); +// handler.sendLogsToThingsboard(client, msg); +// log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); +// if (rpcRequest != null) { +// String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); +// handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// return null; +// } } private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response, @@ -597,7 +710,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } } - private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { + private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest + request, LwM2mClientRpcRequest rpcRequest) { try { Registration registration = lwM2mClient.getRegistration(); LwM2mNode node = ((WriteRequest) request).getNode(); @@ -655,43 +769,46 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { * send operation Execute */ private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) { - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { - lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); - lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { + client.getFwUpdate().setStateUpdate(DOWNLOADED.name()); + client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } - if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { - lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name()); - lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { + client.getSwUpdate().setStateUpdate(DOWNLOADED.name()); + client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } } /** * After finish operation FwSwUpdate Write (error): fw_state = FAILED */ - private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) { - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { - lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); - lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String + msgError) { + LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { + client.getFwUpdate().setStateUpdate(FAILED.name()); + client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } - if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { - lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name()); - lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { + client.getSwUpdate().setStateUpdate(FAILED.name()); + client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } } - private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) { - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) { - lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); + private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String + msgError) { + LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); + if (request.getPath().toString().equals(FW_UPDATE_ID) && client.getFwUpdate() != null) { + client.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); } - if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) { - lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); + if (request.getPath().toString().equals(SW_INSTALL_ID) && client.getSwUpdate() != null) { + client.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); } } - private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { + private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String + observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg); log.warn("[{}]", observeCancelMsg); if (rpcRequest != null) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java index 0beb8ea7cb..40ad57e026 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java @@ -1,4 +1,48 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.transport.lwm2m.server; +import org.eclipse.leshan.core.request.ContentFormat; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + public interface LwM2mDownlinkMsgHandler { + + void sendReadRequest(LwM2mClient client, String targetId, Long timeout); + + void sendReadRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout); + + void sendObserveRequest(LwM2mClient client, String targetId, Long timeout); + + void sendObserveRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout); + + void sendExecuteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback); + + void sendExecuteRequest(LwM2mClient client, String targetId, Object params, Long timeout, DownlinkRequestCallback callback); + + void sendCancelObserveRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback); + + void sendCancelAllRequest(LwM2mClient client, Long timeout, DownlinkRequestCallback callback); + + void sendDiscoverRequest(LwM2mClient client, String targetId, Long timeout); + + void sendWriteAttributesRequest(LwM2mClient client, String targetId, ObjectAttributes params, Long timeout); + + void sendWriteReplaceRequest(LwM2mClient client, String targetIdVer, Object newValue, Long timeout, DownlinkRequestCallback callback); + + void sendWriteUpdateRequest(LwM2mClient client, String targetIdVer, Object newValue, ContentFormat contentFormat, Long timeout, DownlinkRequestCallback callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java index a52fc0158d..9283fa22f5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java @@ -48,6 +48,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -210,28 +211,6 @@ public class LwM2mTransportServerHelper { throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType); } - public static ContentFormat convertResourceModelTypeToContentFormat(ResourceModel.Type type) { - switch (type) { - case BOOLEAN: - case STRING: - case TIME: - case INTEGER: - case FLOAT: - return ContentFormat.TLV; - case OPAQUE: - return ContentFormat.OPAQUE; - case OBJLNK: - return ContentFormat.LINK; - default: - } - throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); - } - - public static ContentFormat getContentFormatByResourceModelType(ResourceModel resourceModel, ContentFormat contentFormat) { - return contentFormat.equals(ContentFormat.TLV) ? convertResourceModelTypeToContentFormat(resourceModel.type) : - contentFormat; - } - public static Object getValueFromKvProto(TransportProtos.KeyValueProto kv) { switch (kv.getType()) { case BOOLEAN_V: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index a6586da0cc..5c210d2af3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -302,7 +302,7 @@ public class LwM2mClient implements Cloneable { Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; resourceModels.forEach((resId, resourceModel) -> { - if (resId == pathIds.getResourceId()) { + if (resId.equals(pathIds.getResourceId())) { resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params, equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type)); @@ -318,8 +318,8 @@ public class LwM2mClient implements Cloneable { Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; resourceModels.forEach((resId, resourceModel) -> { - if (((ConcurrentHashMap) params).containsKey(String.valueOf(resId))) { - Object value = ((ConcurrentHashMap) params).get((String.valueOf(resId))); + if (((Map) params).containsKey(String.valueOf(resId))) { + Object value = ((Map) params).get((String.valueOf(resId))); resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(value, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type)); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 56a601b3d2..efc5aa9657 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import java.util.ArrayList; @@ -156,7 +156,7 @@ public class LwM2mFwSwUpdate { } } - public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request, String pathIdVer) { + public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, String pathIdVer) { if (pathIdVer != null) { this.pendingInfoRequestsStart.remove(pathIdVer); } @@ -176,7 +176,7 @@ public class LwM2mFwSwUpdate { * Send FsSw to Lwm2mClient: * before operation Write: fw_state = DOWNLOADING */ - public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { + public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); @@ -189,8 +189,7 @@ public class LwM2mFwSwUpdate { int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE, - firmwareChunk, handler.config.getTimeout(), this.rpcRequest); + request.sendWriteReplaceRequest(this.lwM2MClient, targetIdVer, firmwareChunk, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { Registration registration = this.getLwM2MClient().getRegistration(); // String api = handler.config.getHostRequests(); @@ -198,8 +197,7 @@ public class LwM2mFwSwUpdate { int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, - uri, handler.config.getTimeout(), this.rpcRequest); + request.sendWriteReplaceRequest(this.lwM2MClient, targetIdVer, uri, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } @@ -230,9 +228,9 @@ public class LwM2mFwSwUpdate { * fw_state/sw_state = UPDATING * send execute */ - public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { + public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); - request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest); + request.sendExecuteRequest(this.lwM2MClient, this.pathInstallId, handler.config.getTimeout(), this.rpcRequest); } /** @@ -381,14 +379,14 @@ public class LwM2mFwSwUpdate { return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; } - private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request) { - request.sendAllRequest(lwM2MClient, - convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, - null, null, 0, null); - request.sendAllRequest(lwM2MClient, - convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, - null, null, 0, null); - } +// private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { +// request.sendAllRequest(lwM2MClient, +// convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, +// null, null, 0, null); +// request.sendAllRequest(lwM2MClient, +// convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, +// null, null, 0, null); +// } public void sendSateOnThingsBoard(DefaultLwM2MUplinkMsgHandler handler) { if (StringUtils.trimToNull(this.stateUpdate) != null) { @@ -401,7 +399,7 @@ public class LwM2mFwSwUpdate { } } - public void sendReadObserveInfo(DefaultLwM2mDownlinkMsgHandler request) { + public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) { this.infoFwSwUpdate = true; this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pathStateId, this.lwM2MClient.getRegistration())); @@ -418,7 +416,7 @@ public class LwM2mFwSwUpdate { this.pathNameId, this.lwM2MClient.getRegistration())); } this.pendingInfoRequestsStart.forEach(pathIdVer -> { - request.sendAllRequest(this.lwM2MClient, pathIdVer, OBSERVE, null, 0, this.rpcRequest); + request.sendObserveRequest(this.lwM2MClient, pathIdVer, 0, this.rpcRequest); }); } @@ -433,7 +431,7 @@ public class LwM2mFwSwUpdate { * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") * - finished operation Execute (FwUpdate) */ - public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, DefaultLwM2mDownlinkMsgHandler request, + public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, Registration registration, String path, int value) { if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java new file mode 100644 index 0000000000..84f1854e1d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public interface DownlinkRequestCallback { + + void onSuccess(LwM2mClient client, String msg); + + void onError(LwM2mClient client, String msg, Exception e); +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index 9a36b79051..12a256ebed 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -117,7 +117,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { switch (currentType) { case INTEGER: log.debug("Trying to convert long value {} to date", value); - /** let's assume we received the millisecond since 1970/1/1 */ + /* let's assume we received the millisecond since 1970/1/1 */ return new Date(((Number) value).longValue() * 1000L); case STRING: log.debug("Trying to convert string value {} to date", value); From 489c67778a4009964b0686c834d22ba591e2a588 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 11 Jun 2021 11:10:06 +0300 Subject: [PATCH 033/151] implemented persisted RPC --- .../main/data/upgrade/3.2.2/schema_update.sql | 12 ++ .../server/actors/ActorSystemContext.java | 23 +++- .../server/actors/device/DeviceActor.java | 2 +- .../device/DeviceActorMessageProcessor.java | 109 +++++++++++++++--- .../server/controller/BaseController.java | 18 +++ .../server/controller/RpcController.java | 19 ++- .../action/RuleEngineEntityActionService.java | 2 +- .../rpc/DefaultTbCoreDeviceRpcService.java | 1 + .../rpc/DefaultTbRuleEngineRpcService.java | 2 +- .../server/service/rpc/TbRpcService.java | 69 +++++++++++ .../service/security/AccessValidator.java | 25 ++++ .../permission/CustomerUserPermissions.java | 19 +++ .../service/security/permission/Resource.java | 3 +- .../permission/TenantAdminPermissions.java | 1 + .../server/dao/rpc/RpcService.java | 37 ++++++ .../server/common/data/EntityType.java | 2 +- .../common/data/id/EntityIdFactory.java | 2 + .../server/common/data/id/RpcId.java | 39 +++++++ .../server/common/data/rpc/Rpc.java | 54 +++++++++ .../server/common/data/rpc/RpcStatus.java | 20 ++++ .../common/msg/rpc/ToDeviceRpcRequest.java | 1 + common/queue/src/main/proto/queue.proto | 10 ++ .../transport/coap/CoapTransportResource.java | 35 +++++- .../transport/http/DeviceApiController.java | 40 +++++-- .../secure/LwM2MBootstrapSecurityStore.java | 2 +- .../DefaultLwM2MTransportMsgHandler.java | 4 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 34 ++++-- .../transport/mqtt/MqttTransportHandler.java | 30 ++++- .../mqtt/session/GatewayDeviceSessionCtx.java | 36 +++++- .../mqtt/session/GatewaySessionHandler.java | 7 +- .../snmp/session/DeviceSessionContext.java | 17 +++ .../common/transport/TransportService.java | 3 + .../service/DefaultTransportService.java | 9 ++ .../server/dao/model/ModelConstants.java | 11 ++ .../server/dao/model/sql/RpcEntity.java | 102 ++++++++++++++++ .../server/dao/rpc/BaseRpcService.java | 60 ++++++++++ .../thingsboard/server/dao/rpc/RpcDao.java | 27 +++++ .../server/dao/sql/rpc/JpaRpcDao.java | 56 +++++++++ .../server/dao/sql/rpc/RpcRepository.java | 28 +++++ .../resources/sql/schema-entities-hsql.sql | 11 ++ .../main/resources/sql/schema-entities.sql | 11 ++ .../resources/sql/hsql/drop-all-tables.sql | 1 + .../resources/sql/psql/drop-all-tables.sql | 1 + .../api/RuleEngineDeviceRpcRequest.java | 1 + .../rule/engine/rpc/TbSendRPCRequestNode.java | 4 + 45 files changed, 940 insertions(+), 60 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/RpcId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 5647c301cf..dec3472970 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -135,3 +135,15 @@ $$; ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32); UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL; + +CREATE TABLE IF NOT EXISTS rpc ( + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + device_id uuid NOT NULL, + expiration_time bigint NOT NULL, + request varchar(10000000) NOT NULL, + response varchar(10000000), + status varchar(255) NOT NULL +); + diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index b52f85af92..5f97f3151f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleNodeStateService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; import org.thingsboard.server.service.executors.SharedEventLoopGroupService; import org.thingsboard.server.service.mail.MailExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; +import org.thingsboard.server.service.rpc.TbRpcService; import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; import org.thingsboard.server.service.script.JsExecutorService; import org.thingsboard.server.service.script.JsInvokeService; @@ -303,23 +304,33 @@ public class ActorSystemContext { @Lazy @Autowired(required = false) - @Getter private EdgeService edgeService; + @Getter + private EdgeService edgeService; @Lazy @Autowired(required = false) - @Getter private EdgeEventService edgeEventService; + @Getter + private EdgeEventService edgeEventService; @Lazy @Autowired(required = false) - @Getter private EdgeRpcService edgeRpcService; + @Getter + private EdgeRpcService edgeRpcService; @Lazy @Autowired(required = false) - @Getter private ResourceService resourceService; + @Getter + private ResourceService resourceService; @Lazy @Autowired(required = false) - @Getter private OtaPackageService otaPackageService; + @Getter + private OtaPackageService otaPackageService; + + @Lazy + @Autowired(required = false) + @Getter + private TbRpcService tbRpcService; @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java index 6e2761c9c5..3e06b8387c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java @@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor { super.init(ctx); log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); try { - processor.initSessionTimeout(ctx); + processor.init(ctx); log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); } catch (Exception e) { log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e); diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 5af11d87f5..0c36fad123 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RpcError; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; @@ -38,12 +39,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; @@ -52,8 +58,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; @@ -68,10 +74,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; +import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; @@ -162,19 +170,23 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { ToDeviceRpcRequest request = msg.getMsg(); - ToDeviceRpcRequestBody body = request.getBody(); - ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder() - .setRequestId(rpcSeq++) - .setMethodName(body.getMethod()) - .setParams(body.getParams()) - .setExpirationTime(request.getExpirationTime()) - .setRequestIdMSB(request.getId().getMostSignificantBits()) - .setRequestIdLSB(request.getId().getLeastSignificantBits()) - .build(); + ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request); long timeout = request.getExpirationTime() - System.currentTimeMillis(); + boolean persisted = request.isPersisted(); if (timeout <= 0) { log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); + + if (persisted) { + Rpc rpc = new Rpc(new RpcId(request.getId())); + rpc.setCreatedTime(System.currentTimeMillis()); + rpc.setTenantId(tenantId); + rpc.setDeviceId(deviceId); + rpc.setExpirationTime(request.getExpirationTime()); + rpc.setRequest(JacksonUtil.valueToTree(request)); + rpc.setStatus(RpcStatus.TIMEOUT); + systemContext.getTbRpcService().save(tenantId, rpc); + } return; } @@ -195,6 +207,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { syncSessionSet.forEach(rpcSubscriptions::remove); } + if (persisted) { + Rpc rpc = new Rpc(new RpcId(request.getId())); + rpc.setCreatedTime(System.currentTimeMillis()); + rpc.setTenantId(tenantId); + rpc.setDeviceId(deviceId); + rpc.setExpirationTime(request.getExpirationTime()); + rpc.setRequest(JacksonUtil.valueToTree(request)); + rpc.setStatus(sent ? RpcStatus.SENT : RpcStatus.QUEUED); + systemContext.getTbRpcService().save(tenantId, rpc); + if (!(sent || request.isOneway())) { + ObjectNode response = JacksonUtil.newObjectNode(); + response.put("rpcId", request.getId().toString()); + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null)); + } + } + if (request.isOneway() && sent) { log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); @@ -208,6 +236,20 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) { + ToDeviceRpcRequestBody body = request.getBody(); + return ToDeviceRpcRequestMsg.newBuilder() + .setRequestId(rpcSeq++) + .setMethodName(body.getMethod()) + .setParams(body.getParams()) + .setExpirationTime(request.getExpirationTime()) + .setRequestIdMSB(request.getId().getMostSignificantBits()) + .setRequestIdLSB(request.getId().getLeastSignificantBits()) + .setOneway(request.isOneway()) + .setPersisted(request.isPersisted()) + .build(); + } + void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) { log.debug("[{}] Processing rpc command response from edge session", deviceId); ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); @@ -229,6 +271,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); if (requestMd != null) { log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null); systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); } @@ -270,7 +313,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { .setExpirationTime(request.getExpirationTime()) .setRequestIdMSB(request.getId().getMostSignificantBits()) .setRequestIdLSB(request.getId().getLeastSignificantBits()) + .setOneway(request.isOneway()) + .setPersisted(request.isPersisted()) .build(); + + if (request.isPersisted()) { + systemContext.getTbRpcService().save(tenantId, new RpcId(request.getId()), RpcStatus.SENT, null); + } sendToTransport(rpcRequest, sessionId, nodeId); }; } @@ -299,10 +348,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { if (msg.hasClaimDevice()) { handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice()); } + if (msg.hasPersistedRpcResponseMsg()) { + processPersistedRpcResponses(context, msg.getSessionInfo(), msg.getPersistedRpcResponseMsg()); + } callback.onSuccess(); } - private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) { + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) { DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); } @@ -441,11 +493,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { if (success) { systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), responseMsg.getPayload(), null)); + if (requestMd.getMsg().getMsg().isPersisted()) { + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.SUCCESSFUL, JacksonUtil.toJsonNode(responseMsg.getPayload())); + } } else { log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); + if (requestMd.getMsg().getMsg().isPersisted()) { + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.FAILED, JacksonUtil.toJsonNode(responseMsg.getPayload())); + } } } + private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) { + UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB()); + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), RpcStatus.valueOf(responseMsg.getStatus()), null); + } + private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { @@ -564,7 +627,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) { log.info("2) LwM2Mtype: "); - TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder(); + ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder(); notification.addCredentialsId(deviceCredentials.getCredentialsId()); notification.addCredentialsValue(deviceCredentials.getCredentialsValue()); ToTransportMsg msg = ToTransportMsg.newBuilder() @@ -639,7 +702,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { ListenableFuture future = systemContext.getEdgeEventService().saveAsync(edgeEvent); Futures.addCallback(future, new FutureCallback() { @Override - public void onSuccess( EdgeEvent result) { + public void onSuccess(EdgeEvent result) { systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId); } @@ -755,8 +818,26 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { .addAllSessions(sessionsList).build().toByteArray()); } - void initSessionTimeout(TbActorCtx ctx) { + void init(TbActorCtx ctx) { schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout()); + PageLink pageLink = new PageLink(10); + PageData pageData; + do { + pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(deviceId, RpcStatus.QUEUED, pageLink); + pageData.getData().forEach(rpc -> { + ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); + long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); + if (timeout <= 0) { + rpc.setStatus(RpcStatus.TIMEOUT); + systemContext.getTbRpcService().save(tenantId, rpc); + } else { + registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout); + } + }); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); } void checkSessionsTimeout() { diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 7d41deb2ab..a731e806c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -69,6 +69,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -83,6 +84,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; @@ -106,6 +108,7 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -245,6 +248,9 @@ public abstract class BaseController { @Autowired protected OtaPackageStateService otaPackageStateService; + @Autowired + protected RpcService rpcService; + @Autowired protected TbQueueProducerProvider producerProvider; @@ -786,6 +792,18 @@ public abstract class BaseController { } } + Rpc checkRpcId(RpcId rpcId, Operation operation) throws ThingsboardException { + try { + validateId(rpcId, "Incorrect rpcId " + rpcId); + Rpc rpc = rpcService.findById(getCurrentUser().getTenantId(), rpcId); + checkNotNull(rpc); + accessControlService.checkPermission(getCurrentUser(), Resource.RPC, operation, rpcId, rpc); + return rpc; + } catch (Exception e) { + throw handleException(e, false); + } + } + @SuppressWarnings("unchecked") protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcController.java b/application/src/main/java/org/thingsboard/server/controller/RpcController.java index e71ffbc6d2..8a75aad485 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcController.java @@ -38,8 +38,10 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -93,6 +95,19 @@ public class RpcController extends BaseController { return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/persisted/{rpcId}", method = RequestMethod.GET) + @ResponseBody + public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException { + checkParameter("RpcId", strRpc); + try { + RpcId rpcId = new RpcId(UUID.fromString(strRpc)); + return checkRpcId(rpcId, Operation.READ); + } catch (Exception e) { + throw handleException(e); + } + } + private DeferredResult handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { try { JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); @@ -103,6 +118,7 @@ public class RpcController extends BaseController { long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout; long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID(); + boolean persisted = rpcRequestBody.has("persisted") && rpcRequestBody.get("persisted").asBoolean(); accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback>() { @Override public void onSuccess(@Nullable DeferredResult result) { @@ -111,7 +127,8 @@ public class RpcController extends BaseController { deviceId, oneWay, expTime, - body + body, + persisted ); deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser); } diff --git a/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java index f1320d1a7a..754d32b704 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java @@ -54,7 +54,7 @@ public class RuleEngineEntityActionService { private static final ObjectMapper json = new ObjectMapper(); - public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, + public void pushEntityActionToRuleEngine(EntityId entityId, Object entity, TenantId tenantId, CustomerId customerId, ActionType actionType, User user, Object... additionalInfo) { String msgType = null; switch (actionType) { diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index 85c26d4c4d..a69402201a 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -157,6 +157,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { metaData.putValue("originServiceId", serviceId); metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); metaData.putValue("oneway", Boolean.toString(msg.isOneway())); + metaData.putValue("persisted", Boolean.toString(msg.isPersisted())); Device device = deviceService.findDeviceById(msg.getTenantId(), msg.getDeviceId()); if (device != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java index c3b038c35a..e16af3695b 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java @@ -100,7 +100,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi @Override public void sendRpcRequestToDevice(RuleEngineDeviceRpcRequest src, Consumer consumer) { ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(), - src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); + src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted()); forwardRpcRequestToDeviceActor(request, response -> { if (src.isRestApiCall()) { sendRpcResponseToTbCore(src.getOriginServiceId(), response); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java new file mode 100644 index 0000000000..d85223c43a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.rpc; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.RpcId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.rpc.RpcService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.action.RuleEngineEntityActionService; + +@TbCoreComponent +@Service +@RequiredArgsConstructor +@Slf4j +public class TbRpcService { + private final RpcService rpcService; + private final RuleEngineEntityActionService ruleEngineEntityActionService; + + public void save(TenantId tenantId, Rpc rpc) { + Rpc saved = rpcService.save(tenantId, rpc); + ruleEngineEntityActionService.pushEntityActionToRuleEngine(saved.getId(), saved, tenantId, null, rpc.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + } + + public void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode response) { + Rpc foundRpc = rpcService.findById(tenantId, rpcId); + if (foundRpc != null) { + foundRpc.setStatus(newStatus); + if (response != null) { + foundRpc.setResponse(response); + } + Rpc saved = rpcService.save(tenantId, foundRpc); + ruleEngineEntityActionService.pushEntityActionToRuleEngine(saved.getId(), saved, tenantId, null, ActionType.UPDATED, null); + } else { + log.warn("[{}] Failed to update RPC status because RPC was already deleted", rpcId); + } + } + + public Rpc findRpcById(TenantId tenantId, RpcId rpcId) { + return rpcService.findById(tenantId, rpcId); + } + + public PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + return rpcService.findAllByDeviceIdAndStatus(deviceId, rpcStatus, pageLink); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index 540a47e8b0..ab9d0de231 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -47,11 +47,13 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.controller.HttpValidationCallback; @@ -65,6 +67,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; @@ -137,6 +140,9 @@ public class AccessValidator { @Autowired protected OtaPackageService otaPackageService; + @Autowired + protected RpcService rpcService; + private ExecutorService executor; @PostConstruct @@ -235,6 +241,9 @@ public class AccessValidator { case OTA_PACKAGE: validateOtaPackage(currentUser, operation, entityId, callback); return; + case RPC: + validateRpc(currentUser, operation, entityId, callback); + return; default: //TODO: add support of other entities throw new IllegalStateException("Not Implemented!"); @@ -261,6 +270,22 @@ public class AccessValidator { } } + private void validateRpc(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { + ListenableFuture rpcFurure = rpcService.findRpcByIdAsync(currentUser.getTenantId(), new RpcId(entityId.getId())); + Futures.addCallback(rpcFurure, getCallback(callback, rpc -> { + if (rpc == null) { + return ValidationResult.entityNotFound("Rpc with requested id wasn't found!"); + } else { + try { + accessControlService.checkPermission(currentUser, Resource.RPC, operation, entityId, rpc); + } catch (ThingsboardException e) { + return ValidationResult.accessDenied(e.getMessage()); + } + return ValidationResult.ok(rpc); + } + }), executor); + } + private void validateDeviceProfile(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { if (currentUser.isSystemAdmin()) { callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index fc1705d688..b0c39a72d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -41,6 +41,7 @@ public class CustomerUserPermissions extends AbstractPermissions { put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); put(Resource.WIDGET_TYPE, widgetsPermissionChecker); put(Resource.EDGE, customerEntityPermissionChecker); + put(Resource.RPC, rpcPermissionChecker); } private static final PermissionChecker customerEntityPermissionChecker = @@ -138,4 +139,22 @@ public class CustomerUserPermissions extends AbstractPermissions { } }; + + private static final PermissionChecker rpcPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) { + + @Override + @SuppressWarnings("unchecked") + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (!super.hasPermission(user, operation, entityId, entity)) { + return false; + } + if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) { + return true; + } + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + return true; + } + }; } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 43c420a94a..c2890836b4 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -39,7 +39,8 @@ public enum Resource { API_USAGE_STATE(EntityType.API_USAGE_STATE), TB_RESOURCE(EntityType.TB_RESOURCE), OTA_PACKAGE(EntityType.OTA_PACKAGE), - EDGE(EntityType.EDGE); + EDGE(EntityType.EDGE), + RPC(EntityType.RPC); private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 8b4d44e938..ad753898e9 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -44,6 +44,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.TB_RESOURCE, tbResourcePermissionChecker); put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker); put(Resource.EDGE, tenantEntityPermissionChecker); + put(Resource.RPC, tenantEntityPermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java new file mode 100644 index 0000000000..22446d143e --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.rpc; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.RpcId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; + +public interface RpcService { + Rpc save(TenantId tenantId, Rpc rpc); + + void remove(TenantId tenantId, RpcId id); + + Rpc findById(TenantId tenantId, RpcId id); + + ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId id); + + PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index cf6c6fd9a7..224af1fc3b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index a4b2327c75..6d8ddfdd18 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -75,6 +75,8 @@ public class EntityIdFactory { return new OtaPackageId(uuid); case EDGE: return new EdgeId(uuid); + case RPC: + return new RpcId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/RpcId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/RpcId.java new file mode 100644 index 0000000000..a4cd8797b0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/RpcId.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public final class RpcId extends UUIDBased implements EntityId { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public RpcId(@JsonProperty("id") UUID id) { + super(id); + } + + @JsonIgnore + @Override + public EntityType getEntityType() { + return EntityType.RPC; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java new file mode 100644 index 0000000000..504c89b569 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/Rpc.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.rpc; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.RpcId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@EqualsAndHashCode(callSuper = true) +public class Rpc extends BaseData implements HasTenantId { + private TenantId tenantId; + private DeviceId deviceId; + private long expirationTime; + private JsonNode request; + private JsonNode response; + private RpcStatus status; + + public Rpc() { + super(); + } + + public Rpc(RpcId id) { + super(id); + } + + public Rpc(Rpc rpc) { + super(rpc); + this.tenantId = rpc.getTenantId(); + this.deviceId = rpc.getDeviceId(); + this.expirationTime = rpc.getExpirationTime(); + this.request = rpc.getRequest(); + this.response = rpc.getResponse(); + this.status = rpc.getStatus(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java new file mode 100644 index 0000000000..fea9e30a75 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.rpc; + +public enum RpcStatus { + QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, FAILED +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java index 3cce7b1d33..b3b33146d0 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java @@ -34,5 +34,6 @@ public class ToDeviceRpcRequest implements Serializable { private final boolean oneway; private final long expirationTime; private final ToDeviceRpcRequestBody body; + private final boolean persisted; } diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index faf88c5620..03311a11f9 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -323,6 +323,8 @@ message ToDeviceRpcRequestMsg { int64 expirationTime = 4; int64 requestIdMSB = 5; int64 requestIdLSB = 6; + bool oneway = 7; + bool persisted = 8; } message ToDeviceRpcResponseMsg { @@ -330,6 +332,13 @@ message ToDeviceRpcResponseMsg { string payload = 2; } +message ToDevicePersistedRpcResponseMsg { + int32 requestId = 1; + int64 requestIdMSB = 2; + int64 requestIdLSB = 3; + string status = 4; +} + message ToServerRpcRequestMsg { int32 requestId = 1; string methodName = 2; @@ -433,6 +442,7 @@ message TransportToDeviceActorMsg { SubscriptionInfoProto subscriptionInfo = 7; ClaimDeviceMsg claimDevice = 8; ProvisionDeviceRequestMsg provisionDevice = 9; + ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10; } message TransportToRuleEngineMsg { diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index cd80aa42df..d8fe57446c 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadCon import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -337,14 +338,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { break; case TO_SERVER_RPC_REQUEST: transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout); + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout); transportService.process(sessionInfo, coapTransportAdaptor.convertToServerRpcRequest(sessionId, request), new CoapNoOpCallback(exchange)); break; case GET_ATTRIBUTES_REQUEST: transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout); + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout); transportService.process(sessionInfo, coapTransportAdaptor.convertToGetAttributes(sessionId, request), new CoapNoOpCallback(exchange)); @@ -383,12 +384,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) { tokenToSessionInfoMap.putIfAbsent(token, sessionInfo); - transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder)); + transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo)); transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); } - private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) { - return new CoapSessionListener(this, exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder); + private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo) { + return new CoapSessionListener(this, exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo); } private String getTokenFromRequest(Request request) { @@ -510,12 +511,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private final CoapExchange exchange; private final CoapTransportAdaptor coapTransportAdaptor; private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder; + private final TransportProtos.SessionInfoProto sessionInfo; - CoapSessionListener(CoapTransportResource coapTransportResource, CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) { + CoapSessionListener(CoapTransportResource coapTransportResource, CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo) { this.coapTransportResource = coapTransportResource; this.exchange = exchange; this.coapTransportAdaptor = coapTransportAdaptor; this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder; + this.sessionInfo = sessionInfo; } @Override @@ -558,11 +561,31 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { + boolean successful; try { exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder)); + successful = true; } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + successful = false; + } + if (msg.getPersisted()) { + RpcStatus status; + if (successful) { + status = RpcStatus.FAILED; + } else if (msg.getOneway()) { + status = RpcStatus.SUCCESSFUL; + } else { + status = RpcStatus.DELIVERED; + } + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() + .setRequestId(msg.getRequestId()) + .setRequestIdLSB(msg.getRequestIdLSB()) + .setRequestIdMSB(msg.getRequestIdMSB()) + .setStatus(status.name()) + .build(); + coapTransportResource.transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY); } } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index f4a3f705af..cfb25e9d78 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.http; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; @@ -34,9 +35,10 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportService; @@ -95,7 +97,9 @@ public class DeviceApiController implements TbTransportService { request.addAllSharedAttributeNames(sharedKeySet); } TransportService transportService = transportContext.getTransportService(); - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout()); + transportService.registerSyncSession(sessionInfo, + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo), + transportContext.getDefaultTimeout()); transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); })); return responseWriter; @@ -151,7 +155,8 @@ public class DeviceApiController implements TbTransportService { transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { TransportService transportService = transportContext.getTransportService(); - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), + transportService.registerSyncSession(sessionInfo, + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo), timeout == 0 ? transportContext.getDefaultTimeout() : timeout); transportService.process(sessionInfo, SubscribeToRPCMsg.getDefaultInstance(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); @@ -181,7 +186,9 @@ public class DeviceApiController implements TbTransportService { new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { JsonObject request = new JsonParser().parse(json).getAsJsonObject(); TransportService transportService = transportContext.getTransportService(); - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout()); + transportService.registerSyncSession(sessionInfo, + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo), + transportContext.getDefaultTimeout()); transportService.process(sessionInfo, ToServerRpcRequestMsg.newBuilder().setRequestId(0) .setMethodName(request.get("method").getAsString()) .setParams(request.get("params").toString()).build(), @@ -198,7 +205,8 @@ public class DeviceApiController implements TbTransportService { transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { TransportService transportService = transportContext.getTransportService(); - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), + transportService.registerSyncSession(sessionInfo, + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo), timeout == 0 ? transportContext.getDefaultTimeout() : timeout); transportService.process(sessionInfo, SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); @@ -372,13 +380,12 @@ public class DeviceApiController implements TbTransportService { } } + @RequiredArgsConstructor private static class HttpSessionListener implements SessionMsgListener { private final DeferredResult responseWriter; - - HttpSessionListener(DeferredResult responseWriter) { - this.responseWriter = responseWriter; - } + private final TransportService transportService; + private final SessionInfoProto sessionInfo; @Override public void onGetAttributesResponse(GetAttributeResponseMsg msg) { @@ -399,6 +406,21 @@ public class DeviceApiController implements TbTransportService { @Override public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) { responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); + if (msg.getPersisted()) { + RpcStatus status; + if (msg.getOneway()) { + status = RpcStatus.SUCCESSFUL; + } else { + status = RpcStatus.DELIVERED; + } + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() + .setRequestId(msg.getRequestId()) + .setRequestIdLSB(msg.getRequestIdLSB()) + .setRequestIdMSB(msg.getRequestIdMSB()) + .setStatus(status.name()) + .build(); + transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY); + } } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index ccc2e62117..1e7b272174 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -161,7 +161,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo, context.getTransportService())); if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index e0a85bc77d..b211e49d50 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -188,7 +188,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (lwM2MClient != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo, transportService)); TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) @@ -1320,7 +1320,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler */ private void reportActivityAndRegister(SessionInfoProto sessionInfo) { if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo, transportService)); this.reportActivitySubscription(sessionInfo); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index b71de7db1b..1636a1ae6c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -17,12 +17,16 @@ package org.thingsboard.server.transport.lwm2m.server; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.transport.SessionMsgListener; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; @@ -35,14 +39,11 @@ import java.util.Optional; import java.util.UUID; @Slf4j +@RequiredArgsConstructor public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener { - private DefaultLwM2MTransportMsgHandler handler; - private TransportProtos.SessionInfoProto sessionInfo; - - public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) { - this.handler = handler; - this.sessionInfo = sessionInfo; - } + private final DefaultLwM2MTransportMsgHandler handler; + private final TransportProtos.SessionInfoProto sessionInfo; + private final TransportService transportService; @Override public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { @@ -52,7 +53,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener { + ChannelFuture channelFuture = deviceSessionCtx.getChannel().writeAndFlush(payload); + if (rpcRequest.getPersisted()) { + channelFuture.addListener(future -> { + RpcStatus status; + Throwable t = future.cause(); + if (t != null) { + log.error("Failed delivering RPC command to device!", t); + status = RpcStatus.FAILED; + } else if (rpcRequest.getOneway()) { + status = RpcStatus.SUCCESSFUL; + } else { + status = RpcStatus.DELIVERED; + } + TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() + .setRequestId(rpcRequest.getRequestId()) + .setRequestIdLSB(rpcRequest.getRequestIdLSB()) + .setRequestIdMSB(rpcRequest.getRequestIdMSB()) + .setStatus(status.name()) + .build(); + transportService.process(deviceSessionCtx.getSessionInfo(), msg, TransportServiceCallback.EMPTY); + }); + } + }); } catch (Exception e) { log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 707586b7de..9e3633cb4b 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -15,9 +15,13 @@ */ package org.thingsboard.server.transport.mqtt.session; +import io.netty.channel.ChannelFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.transport.SessionMsgListener; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; @@ -32,9 +36,11 @@ import java.util.concurrent.ConcurrentMap; public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { private final GatewaySessionHandler parent; + private final TransportService transportService; public GatewayDeviceSessionCtx(GatewaySessionHandler parent, TransportDeviceInfo deviceInfo, - DeviceProfile deviceProfile, ConcurrentMap mqttQoSMap) { + DeviceProfile deviceProfile, ConcurrentMap mqttQoSMap, + TransportService transportService) { super(UUID.randomUUID(), mqttQoSMap); this.parent = parent; setSessionInfo(SessionInfoProto.newBuilder() @@ -56,6 +62,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple .build()); setDeviceInfo(deviceInfo); setDeviceProfile(deviceProfile); + this.transportService = transportService; } @Override @@ -89,7 +96,32 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) { try { - parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush); + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent( + payload -> { + ChannelFuture channelFuture = parent.writeAndFlush(payload); + if (request.getPersisted()) { + channelFuture.addListener(future -> { + RpcStatus status; + Throwable t = future.cause(); + if (t != null) { + log.error("Failed delivering RPC command to device!", t); + status = RpcStatus.FAILED; + } else if (request.getOneway()) { + status = RpcStatus.SUCCESSFUL; + } else { + status = RpcStatus.DELIVERED; + } + TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() + .setRequestId(request.getRequestId()) + .setRequestIdLSB(request.getRequestIdLSB()) + .setRequestIdMSB(request.getRequestIdMSB()) + .setStatus(status.name()) + .build(); + transportService.process(getSessionInfo(), msg, TransportServiceCallback.EMPTY); + }); + } + } + ); } catch (Exception e) { log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 7882ad2410..38d4232492 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -28,6 +28,7 @@ import com.google.gson.JsonSyntaxException; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolStringList; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttPublishMessage; @@ -188,8 +189,8 @@ public class GatewaySessionHandler { } } - void writeAndFlush(MqttMessage mqttMessage) { - channel.writeAndFlush(mqttMessage); + ChannelFuture writeAndFlush(MqttMessage mqttMessage) { + return channel.writeAndFlush(mqttMessage); } int nextMsgId() { @@ -251,7 +252,7 @@ public class GatewaySessionHandler { new TransportServiceCallback() { @Override public void onSuccess(GetOrCreateDeviceFromGatewayResponse msg) { - GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), msg.getDeviceProfile(), mqttQoSMap); + GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), msg.getDeviceProfile(), mqttQoSMap, transportService); if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { log.trace("[{}] First got or created device [{}], type [{}] for the gateway session", sessionId, deviceName, deviceType); SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java index ce70ece5ff..da459214c0 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java @@ -26,7 +26,9 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.transport.SessionMsgListener; +import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; @@ -139,6 +141,21 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @Override public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); + if (toDeviceRequest.getPersisted()) { + RpcStatus status; + if (toDeviceRequest.getOneway()) { + status = RpcStatus.SUCCESSFUL; + } else { + status = RpcStatus.DELIVERED; + } + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() + .setRequestId(toDeviceRequest.getRequestId()) + .setRequestIdLSB(toDeviceRequest.getRequestIdLSB()) + .setRequestIdMSB(toDeviceRequest.getRequestIdMSB()) + .setStatus(status.name()) + .build(); + snmpTransportContext.getTransportService().process(getSessionInfo(), responseMsg, TransportServiceCallback.EMPTY); + } } @Override diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index 4a4e68f64f..e692d3b002 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.service.SessionMetaData; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; @@ -109,6 +110,8 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback callback); + void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback callback); void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback callback); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 77b75dc07a..b4e536a49b 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -557,6 +557,15 @@ public class DefaultTransportService implements TransportService { } } + @Override + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback callback) { + if (checkLimits(sessionInfo, msg, callback)) { + reportActivityInternal(sessionInfo); + sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPersistedRpcResponseMsg(msg).build(), + new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback)); + } + } + private void processTimeout(String requestId) { RpcRequestMetadata data = toServerRpcPendingMap.remove(requestId); if (data != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index bfb530dd35..483841f4fb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -496,6 +496,17 @@ public class ModelConstants { public static final String OTA_PACKAGE_DATA_SIZE_COLUMN = "data_size"; public static final String OTA_PACKAGE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; + /** + * Persisted RPC constants. + */ + public static final String RPC_TABLE_NAME = "rpc"; + public static final String RPC_TENANT_ID_COLUMN = TENANT_ID_COLUMN; + public static final String RPC_DEVICE_ID = "device_id"; + public static final String RPC_EXPIRATION_TIME = "expiration_time"; + public static final String RPC_REQUEST = "request"; + public static final String RPC_RESPONSE = "response"; + public static final String RPC_STATUS = "status"; + /** * Edge constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java new file mode 100644 index 0000000000..a8823cb8cd --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RpcEntity.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.RpcId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.RPC_DEVICE_ID; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_EXPIRATION_TIME; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_REQUEST; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_RESPONSE; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_STATUS; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RPC_TENANT_ID_COLUMN; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = RPC_TABLE_NAME) +public class RpcEntity extends BaseSqlEntity implements BaseEntity { + + @Column(name = RPC_TENANT_ID_COLUMN) + private UUID tenantId; + + @Column(name = RPC_DEVICE_ID) + private UUID deviceId; + + @Column(name = RPC_EXPIRATION_TIME) + private long expirationTime; + + @Type(type = "json") + @Column(name = RPC_REQUEST) + private JsonNode request; + + @Type(type = "json") + @Column(name = RPC_RESPONSE) + private JsonNode response; + + @Enumerated(EnumType.STRING) + @Column(name = RPC_STATUS) + private RpcStatus status; + + public RpcEntity() { + super(); + } + + public RpcEntity(Rpc rpc) { + this.setUuid(rpc.getUuidId()); + this.createdTime = rpc.getCreatedTime(); + this.tenantId = rpc.getTenantId().getId(); + this.deviceId = rpc.getDeviceId().getId(); + this.expirationTime = rpc.getExpirationTime(); + this.request = rpc.getRequest(); + this.response = rpc.getResponse(); + this.status = rpc.getStatus(); + } + + @Override + public Rpc toData() { + Rpc rpc = new Rpc(new RpcId(id)); + rpc.setCreatedTime(createdTime); + rpc.setTenantId(new TenantId(tenantId)); + rpc.setDeviceId(new DeviceId(deviceId)); + rpc.setExpirationTime(expirationTime); + rpc.setRequest(request); + rpc.setResponse(response); + rpc.setStatus(status); + return rpc; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java new file mode 100644 index 0000000000..577263a3bd --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.rpc; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.RpcId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; + +@Service +@Slf4j +@RequiredArgsConstructor +public class BaseRpcService implements RpcService { + private final RpcDao rpcDao; + + @Override + public Rpc save(TenantId tenantId, Rpc rpc) { + return rpcDao.save(tenantId, rpc); + } + + @Override + public void remove(TenantId tenantId, RpcId id) { + rpcDao.removeById(tenantId, id.getId()); + } + + @Override + public Rpc findById(TenantId tenantId, RpcId id) { + return rpcDao.findById(tenantId, id.getId()); + } + + @Override + public ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId id) { + return rpcDao.findByIdAsync(tenantId, id.getId()); + } + + @Override + public PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + return rpcDao.findAllByDeviceId(deviceId, rpcStatus, pageLink); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java new file mode 100644 index 0000000000..753d1043f3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.rpc; + +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.Dao; + +public interface RpcDao extends Dao { + PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java new file mode 100644 index 0000000000..1a538c9d9d --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.rpc; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.RpcEntity; +import org.thingsboard.server.dao.rpc.RpcDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.UUID; + +@Slf4j +@Component +@AllArgsConstructor +public class JpaRpcDao extends JpaAbstractDao implements RpcDao { + + private final RpcRepository rpcRepository; + + @Override + protected Class getEntityClass() { + return RpcEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return rpcRepository; + } + + + @Override + public PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + return DaoUtil.toPageData(rpcRepository.findAllByDeviceIdAndStatus(deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink))); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java new file mode 100644 index 0000000000..bc04aa7e71 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.rpc; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.model.sql.RpcEntity; + +import java.util.UUID; + +public interface RpcRepository extends CrudRepository { + Page findAllByDeviceIdAndStatus(UUID deviceId, RpcStatus status, Pageable pageable); +} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 2c5113867a..65fe412a9a 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -503,3 +503,14 @@ CREATE TABLE IF NOT EXISTS edge_event ( tenant_id uuid, ts bigint NOT NULL ); + +CREATE TABLE IF NOT EXISTS rpc ( + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + device_id uuid NOT NULL, + expiration_time bigint NOT NULL, + request varchar(10000000) NOT NULL, + response varchar(10000000), + status varchar(255) NOT NULL +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 3140ff83d6..1d70bf9f3a 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -541,6 +541,17 @@ CREATE TABLE IF NOT EXISTS edge_event ( ts bigint NOT NULL ); +CREATE TABLE IF NOT EXISTS rpc ( + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + device_id uuid NOT NULL, + expiration_time bigint NOT NULL, + request varchar(10000000) NOT NULL, + response varchar(10000000), + status varchar(255) NOT NULL +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 726b4ba412..57090d34ad 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -32,4 +32,5 @@ DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS ota_package; DROP TABLE IF EXISTS edge; DROP TABLE IF EXISTS edge_event; +DROP TABLE IF EXISTS rpc; DROP FUNCTION IF EXISTS to_uuid; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 855a53df2d..a641fff27e 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -33,3 +33,4 @@ DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS firmware; DROP TABLE IF EXISTS edge; DROP TABLE IF EXISTS edge_event; +DROP TABLE IF EXISTS rpc; diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineDeviceRpcRequest.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineDeviceRpcRequest.java index 856dbf8e60..1d74040b34 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineDeviceRpcRequest.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineDeviceRpcRequest.java @@ -35,6 +35,7 @@ public final class RuleEngineDeviceRpcRequest { private final UUID requestUUID; private final String originServiceId; private final boolean oneway; + private final boolean persisted; private final String method; private final String body; private final long expirationTime; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java index 31bd913f47..1867ae4805 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java @@ -81,6 +81,9 @@ public class TbSendRPCRequestNode implements TbNode { tmp = msg.getMetaData().getValue("oneway"); boolean oneway = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp); + tmp = msg.getMetaData().getValue("persisted"); + boolean persisted = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp); + tmp = msg.getMetaData().getValue("requestUUID"); UUID requestUUID = !StringUtils.isEmpty(tmp) ? UUID.fromString(tmp) : Uuids.timeBased(); tmp = msg.getMetaData().getValue("originServiceId"); @@ -108,6 +111,7 @@ public class TbSendRPCRequestNode implements TbNode { .originServiceId(originServiceId) .expirationTime(expirationTime) .restApiCall(restApiCall) + .persisted(persisted) .build(); ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { From f88cfee2b0f28737b9b0b8a271e7bcd01b980cd5 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 11 Jun 2021 12:29:41 +0300 Subject: [PATCH 034/151] Refactoring of the Downlink Part 2 --- .../server/DefaultLwM2MUplinkMsgHandler.java | 123 ++- .../DefaultLwM2mDownlinkMsgHandler.java | 970 ++++++++---------- .../lwm2m/server/LwM2mDownlinkMsgHandler.java | 50 +- .../lwm2m/server/LwM2mUplinkMsgHandler.java | 2 - .../lwm2m/server/client/LwM2mClient.java | 9 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 46 +- .../AbstractTbLwM2MRequestCallback.java | 40 + ...bstractTbLwM2MTargetedDownlinkRequest.java | 32 + .../downlink/DownlinkRequestCallback.java | 27 +- .../server/downlink/HasContentFormat.java | 23 + .../lwm2m/server/downlink/HasVersionedId.java | 28 + ...bLwM2MCancelAllObserveRequestCallback.java | 36 + .../downlink/TbLwM2MCancelAllRequest.java | 38 + .../downlink/TbLwM2MCancelObserveRequest.java | 37 + .../TbLwM2MCancelObserveRequestCallback.java | 38 + .../server/downlink/TbLwM2MDeleteRequest.java | 37 + .../TbLwM2MDeleteRequestCallback.java | 37 + .../downlink/TbLwM2MDiscoverAllRequest.java | 40 + .../downlink/TbLwM2MDiscoverCallback.java | 37 + .../downlink/TbLwM2MDiscoverRequest.java | 37 + .../downlink/TbLwM2MDownlinkRequest.java | 26 + .../downlink/TbLwM2MExecuteRequest.java | 41 + .../TbLwM2MExecuteRequestCallback.java | 37 + .../downlink/TbLwM2MObserveAllRequest.java | 41 + .../downlink/TbLwM2MObserveRequest.java | 43 + .../TbLwM2MObserveRequestCallback.java | 37 + .../server/downlink/TbLwM2MReadRequest.java | 42 + .../downlink/TbLwM2MReadRequestCallback.java | 37 + .../TbLwM2MWriteAttributesCallback.java | 38 + .../TbLwM2MWriteAttributesRequest.java | 44 + .../downlink/TbLwM2MWriteReplaceCallback.java | 37 + .../downlink/TbLwM2MWriteReplaceRequest.java | 43 + .../downlink/TbLwM2MWriteUpdateRequest.java | 45 + 33 files changed, 1569 insertions(+), 629 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MTargetedDownlinkRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasContentFormat.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index 45e07eaf4c..e6c14b0ba0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -29,6 +29,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; @@ -66,6 +67,22 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -99,11 +116,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -283,18 +296,18 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { //TODO: associate endpointId with device information. } - /** - * Cancel observation for All objects for this registration - */ - @Override - public void setCancelObservationsAll(Registration registration) { - if (registration != null) { - LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); - if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { - defaultLwM2MDownlinkMsgHandler.sendCancelAllRequest(client, this.config.getTimeout(), null); - } - } - } +// /** +// * Cancel observation for All objects for this registration +// */ +// @Override +// public void setCancelObservationsAll(Registration registration) { +// if (registration != null) { +// LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); +// if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { +// defaultLwM2MDownlinkMsgHandler.sendCancelAllRequest(client, TbLwM2MCancelAllRequest.builder().build(), new TbLwM2MCancelAllObserveRequestCallback(this, client)); +// } +// } +// } /** * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource @@ -321,6 +334,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } if (rpcRequest != null) { + //TODO: move this to separate callback. this.sendRpcRequestAfterReadResponse(registration, lwM2MClient, path, response, rpcRequest); } } @@ -466,10 +480,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); } else { - defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), - null, - lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), - this.config.getTimeout(), lwm2mClientRpcRequest); + //TODO: use different methods and RPC callback wrapper. +// defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), +// null, +// lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), +// this.config.getTimeout(), lwm2mClientRpcRequest); } } else { this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); @@ -594,7 +609,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { // #2 lwM2MClient.getPendingReadRequests().addAll(supportedObjects); - supportedObjects.forEach(path -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, path, this.config.getTimeout())); + supportedObjects.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId)); } // #1 this.sendReadRequests(lwM2MClient, profile, supportedObjects); @@ -610,7 +625,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds.addAll(profile.getObserveAttr().getTelemetry()); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId)); } private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { @@ -618,7 +633,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId)); } private void sendWriteAttributeRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { @@ -626,16 +641,40 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { attributesMap = attributesMap.entrySet().stream().filter(target -> isSupportedTargetId(supportedObjects, target.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - attributesMap.forEach((targetId, params) -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, targetId, params, this.config.getTimeout())); + attributesMap.forEach((targetId, params) -> sendWriteAttributesRequest(lwM2MClient, targetId, params)); } - private void sendDiscoverRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { Set targetIds = profile.getObserveAttr().getAttributeLwm2m().keySet(); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); // TODO: why do we need to put observe into pending read requests? // lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, targetId, this.config.getTimeout())); + targetIds.forEach(targetId -> sendDiscoverRequest(lwM2MClient, targetId)); + } + + private void sendDiscoverRequest(LwM2mClient lwM2MClient, String targetId) { + TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(targetId).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, request, new TbLwM2MDiscoverCallback(this, lwM2MClient, targetId)); + } + + private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, new TbLwM2MReadRequestCallback(this, lwM2MClient, versionedId)); + } + + private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { + TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, new TbLwM2MObserveRequestCallback(this, lwM2MClient, versionedId)); + } + + private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { + TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(targetId).attributes(params).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, request, new TbLwM2MWriteAttributesCallback(this, lwM2MClient, targetId)); + } + + private void sendCancelObserveRequest(String versionedId, LwM2mClient client) { + TbLwM2MCancelObserveRequest request = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveRequestCallback(this, client, versionedId)); } /** @@ -690,7 +729,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); + LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(this, clientContext); log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); fwUpdate.updateStateOta(this, defaultLwM2MDownlinkMsgHandler, registration, path, ((Long) lwM2mResource.getValue()).intValue()); } @@ -935,27 +974,27 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (!newObjectsToRead.isEmpty()) { Set newObjectsToReadButNotNewInObserve = diffSets(observeToAdd, newObjectsToRead); // update value in Resources - for (String targetId : newObjectsToReadButNotNewInObserve) { - clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendReadRequest(client, targetId, this.config.getTimeout())); + for (String versionedId : newObjectsToReadButNotNewInObserve) { + clients.forEach(client -> sendReadRequest(client, versionedId)); } } // Calculating difference between old and new flags. if (!observeToAdd.isEmpty()) { for (String targetId : observeToAdd) { - clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendObserveRequest(client, targetId, this.config.getTimeout())); + clients.forEach(client -> sendObserveRequest(client, targetId)); } } if (!observeToRemove.isEmpty()) { for (String targetId : observeToRemove) { - clients.forEach(client -> defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, targetId, this.config.getTimeout(), null)); + clients.forEach(client -> sendCancelObserveRequest(targetId, client)); } } } } /** - Returns new set with elements that are present in set B(new) but absent in set A(old). + * Returns new set with elements that are present in set B(new) but absent in set A(old). */ private static Set diffSets(Set a, Set b) { return b.stream().filter(p -> !a.contains(p)).collect(Collectors.toSet()); @@ -974,7 +1013,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { /** * #6.1 - send update WriteAttribute * #6.2 - send empty WriteAttribute - * */ private void compareAndSendWriteAttributes(List clients, Map lwm2mAttributesOld, Map lwm2mAttributesNew) { ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); @@ -997,7 +1035,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - pathSend.forEach(target -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target), this.config.getTimeout())); + pathSend.forEach(target -> sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target))); } }); } @@ -1008,21 +1046,22 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) .collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - pathSend.forEach(target -> defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(client, target, new ObjectAttributes(), this.config.getTimeout())); + pathSend.forEach(target -> sendWriteAttributesRequest(client, target, new ObjectAttributes())); } }); } } - private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String path) { + private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { - defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, path, newValue, this.config.getTimeout(), null); + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); + defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteReplaceCallback(this, lwM2MClient, versionedId)); } else { - log.error("Failed update resource [{}] [{}]", path, newValue); - String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", - LOG_LW2M_ERROR, path, newValue); + log.error("Failed update resource [{}] [{}]", versionedId, newValue); + String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", + LOG_LW2M_ERROR, versionedId, newValue); this.sendLogsToThingsboard(lwM2MClient, logMsg); - log.info("Failed update resource [{}] [{}]", path, newValue); + log.info("Failed update resource [{}] [{}]", versionedId, newValue); } } @@ -1176,7 +1215,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); + LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(DefaultLwM2MUplinkMsgHandler.this, clientContext); if (rpcRequest != null) { fwUpdate.setStateUpdate(INITIATED.name()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java index 3ba3de2b75..b1b52ec3ed 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java @@ -17,18 +17,12 @@ package org.thingsboard.server.transport.lwm2m.server; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.coap.CoAP; -import org.eclipse.californium.core.coap.Response; import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mNode; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; -import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.observation.Observation; @@ -41,13 +35,12 @@ import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.SimpleDownlinkRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.request.WriteRequest; -import org.eclipse.leshan.core.request.exception.ClientSleepingException; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ExecuteResponse; import org.eclipse.leshan.core.response.LwM2mResponse; +import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.ReadResponse; -import org.eclipse.leshan.core.response.ResponseCallback; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.util.Hex; @@ -61,6 +54,19 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.HasContentFormat; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -70,43 +76,18 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; -import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; -import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.STEP; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @Service @@ -120,7 +101,6 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; private final LwM2mClientContext lwM2mClientContext; - private final DefaultLwM2MUplinkMsgHandler handler; @PostConstruct public void init() { @@ -130,100 +110,90 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendReadRequest(LwM2mClient client, String targetId, Long timeout) { - sendReadRequest(client, targetId, client.getDefaultContentFormat(), timeout); - } - - @Override - public void sendReadRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout) { - String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && client.isValidObjectVersion(targetId)) { - sendRequest(client, new ReadRequest(contentFormat, objectId), timeout); + public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback) { + if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { + ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); + sendRequest(client, downlink, request.getTimeout(), callback); } } @Override - public void sendObserveRequest(LwM2mClient client, String targetId, Long timeout) { - sendObserveRequest(client, targetId, client.getDefaultContentFormat(), timeout); - } - - @Override - public void sendObserveRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout) { - String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && client.isValidObjectVersion(targetId)) { - LwM2mPath resultIds = new LwM2mPath(objectId); + public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { + if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { - ObserveRequest request; + ObserveRequest downlink; + ContentFormat contentFormat = getContentFormat(client, request); if (resultIds.isResource()) { - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); } else if (resultIds.isObjectInstance()) { - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); } else { - request = new ObserveRequest(contentFormat, resultIds.getObjectId()); + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); } - log.info("[{}] Send observation: {}.", client.getEndpoint(), targetId); - sendRequest(client, request, timeout); + log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); + sendRequest(client, downlink, request.getTimeout(), callback); } } } @Override - public void sendExecuteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { - sendExecuteRequest(client, targetId, null, timeout, callback); + public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback) { + Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); + Set paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); + callback.onSuccess(paths); } @Override - public void sendExecuteRequest(LwM2mClient client, String targetId, Object params, Long timeout, DownlinkRequestCallback callback) { - String target = fromVersionedIdToObjectId(targetId); - LwM2mPath resultIds = new LwM2mPath(target); - ResourceModel resourceModelExecute = client.getResourceModel(targetId, this.config.getModelProvider()); + public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { + Link[] objectLinks = client.getRegistration().getSortedObjectLinks(); + Set paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); + callback.onSuccess(paths); + } + + @Override + public void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback) { + ResourceModel resourceModelExecute = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); if (resourceModelExecute != null) { - ExecuteRequest request; - if (params != null && !resourceModelExecute.multiple) { - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds)); + ExecuteRequest downlink; + if (request.getParams() != null && !resourceModelExecute.multiple) { + downlink = new ExecuteRequest(request.getVersionedId(), (String) this.converter.convertValue(request.getParams(), resourceModelExecute.type, ResourceModel.Type.STRING, new LwM2mPath(request.getObjectId()))); } else { - request = new ExecuteRequest(target); + downlink = new ExecuteRequest(request.getVersionedId()); } - sendRequest(client, request, timeout); + sendRequest(client, downlink, request.getTimeout(), callback); } } - public void sendDeleteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { - String target = fromVersionedIdToObjectId(targetId); - sendRequest(client, new DeleteRequest(target), timeout); + @Override + public void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback) { + sendRequest(client, new DeleteRequest(request.getObjectId()), request.getTimeout(), callback); } @Override - public void sendCancelObserveRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback) { - int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration(), fromVersionedIdToObjectId(targetId)); - String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), targetId, observeCancelCnt); - if (callback != null) { - callback.onSuccess(client, observeCancelMsg); - } + public void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback) { + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration(), request.getObjectId()); + callback.onSuccess(observeCancelCnt); } @Override - public void sendCancelAllRequest(LwM2mClient client, Long timeout, DownlinkRequestCallback callback) { + public void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback) { int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration()); - String observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), observeCancelCnt); - if (callback != null) { - callback.onSuccess(client, observeCancelMsg); - } + callback.onSuccess(observeCancelCnt); } @Override - public void sendDiscoverRequest(LwM2mClient client, String targetId, Long timeout) { - String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && client.isValidObjectVersion(targetId)) { - sendRequest(client, new DiscoverRequest(objectId), timeout); + public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback) { + if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { + sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); } } @Override - public void sendWriteAttributesRequest(LwM2mClient client, String targetId, ObjectAttributes params, Long timeout) { - String objectId = fromVersionedIdToObjectId(targetId); - if (objectId != null && client.isValidObjectVersion(targetId) && params != null) { + public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback) { + if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId()) && request.getAttributes() != null) { + ObjectAttributes params = request.getAttributes(); List attributes = new LinkedList<>(); // Dimension and Object version are read only attributes. // addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); @@ -234,366 +204,233 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { addAttribute(attributes, LESSER_THAN, params.getLt()); addAttribute(attributes, STEP, params.getSt()); AttributeSet attributeSet = new AttributeSet(attributes); - sendRequest(client, new WriteAttributesRequest(objectId, attributeSet), timeout); + sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); } } @Override - public void sendWriteReplaceRequest(LwM2mClient client, String targetIdVer, Object newValue, Long timeout, DownlinkRequestCallback callback) { - ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); + public void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback) { + ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); if (resourceModelWrite != null) { ContentFormat contentFormat = convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); try { - LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(targetIdVer)); - WriteRequest request = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat, - path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), newValue); - sendRequest(client, request, timeout); + LwM2mPath path = new LwM2mPath(request.getObjectId()); + WriteRequest downlink = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat, + path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), request.getValue()); + sendRequest(client, downlink, request.getTimeout(), callback); } catch (Exception e) { - callback.onError(client, e.getMessage(), e); + callback.onError(e); } + } else { + //TODO: log validation error using callback. } } @Override - public void sendWriteUpdateRequest(LwM2mClient client, String targetIdVer, Object newValue, ContentFormat contentFormat, Long timeout, DownlinkRequestCallback callback) { - LwM2mPath resultIds = new LwM2mPath(fromVersionedIdToObjectId(targetIdVer)); + public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); if (resultIds.isResource()) { /* * send request: path = '/3/0' node == wM2mObjectInstance * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} **/ - Collection resources = client.getNewResourceForInstance(targetIdVer, newValue, this.config.getModelProvider(), this.converter); - ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); - WriteRequest request = new WriteRequest(WriteRequest.Mode.UPDATE, convertResourceModelTypeToContentFormat(client, resourceModelWrite.type), resultIds.getObjectId(), + Collection resources = client.getNewResourceForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); + ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, convertResourceModelTypeToContentFormat(client, resourceModelWrite.type), resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); - sendRequest(client, request, timeout); + sendRequest(client, downlink, request.getTimeout(), callback); } else if (resultIds.isObjectInstance()) { /* * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" * int rscId = resultIds.getObjectInstanceId(); * contentFormat – Format of the payload (TLV or JSON). */ - Collection resources = client.getNewResourcesForInstance(targetIdVer, newValue, this.config.getModelProvider(), this.converter); + Collection resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); if (resources.size() > 0) { - contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : client.getDefaultContentFormat(); - WriteRequest request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); - sendRequest(client, request, timeout); + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : client.getDefaultContentFormat(); + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); + sendRequest(client, downlink, request.getTimeout(), callback); } else { - callback.onError(client, "No resources to update!", new IllegalArgumentException()); + callback.onValidationError("No resources to update!"); } } else { - callback.onError(client, "Update of the root level object is not supported yet", new IllegalArgumentException()); - } - } - - private void addAttribute(List attributes, String attributeName, T value) { - addAttribute(attributes, attributeName, value, null, null); - } - - private void addAttribute(List attributes, String attributeName, T value, Function converter) { - addAttribute(attributes, attributeName, value, null, converter); - } - - private void addAttribute(List attributes, String attributeName, T value, Predicate filter, Function converter) { - if (value != null && ((filter == null) || filter.test(value))) { - attributes.add(new Attribute(attributeName, converter != null ? converter.apply(value) : value)); - } - } - - private static ContentFormat convertResourceModelTypeToContentFormat(LwM2mClient client, ResourceModel.Type type) { - switch (type) { - case BOOLEAN: - case STRING: - case TIME: - case INTEGER: - case FLOAT: - return client.getDefaultContentFormat(); - case OPAQUE: - return ContentFormat.OPAQUE; - case OBJLNK: - return ContentFormat.LINK; - default: + callback.onValidationError("Update of the root level object is not supported yet!"); } - throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); - } - - public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { - sendAllRequest(client, targetIdVer, typeOper, client.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest); - } - - public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, - ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { - Registration registration = client.getRegistration(); - try { - String target = fromVersionedIdToObjectId(targetIdVer); - if (contentFormat == null) { - contentFormat = client.getDefaultContentFormat(); - } - LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; - if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0) { - if (client.isValidObjectVersion(targetIdVer)) { - timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; - SimpleDownlinkRequest request = createRequest(registration, client, typeOper, contentFormat, target, - targetIdVer, resultIds, params, lwm2mClientRpcRequest); - if (request != null) { - try { - this.sendRequest(client, request, timeoutInMs, lwm2mClientRpcRequest); - } catch (ClientSleepingException e) { - SimpleDownlinkRequest finalRequest = request; - long finalTimeoutInMs = timeoutInMs; - LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; - client.getQueuedRequests().add(() -> sendRequest(client, finalRequest, finalTimeoutInMs, finalRpcRequest)); - } catch (Exception e) { - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); - } - } else if (WRITE_UPDATE.name().equals(typeOper.name())) { - if (lwm2mClientRpcRequest != null) { - String errorMsg = String.format("Path %s params is not valid", targetIdVer); - handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); - } - } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { - if (lwm2mClientRpcRequest != null) { - String errorMsg = String.format("Path %s object model is absent", targetIdVer); - handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); - } - } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); - if (lwm2mClientRpcRequest != null) { - ResourceModel resourceModel = client.getResourceModel(targetIdVer, this.config.getModelProvider()); - String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); - } - } - } else if (lwm2mClientRpcRequest != null) { - String errorMsg = String.format("Path %s not found in object version", targetIdVer); - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); - } - } else { - switch (typeOper) { - case OBSERVE_READ_ALL: - case DISCOVER_ALL: - Set paths; - if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { - Set observations = context.getServer().getObservationService().getObservations(registration); - paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); - } else { - assert registration != null; - Link[] objectLinks = registration.getSortedObjectLinks(); - paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); - } - String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, - typeOper.name(), paths); - this.handler.sendLogsToThingsboard(client, msg); - if (lwm2mClientRpcRequest != null) { - String valueMsg = String.format("Paths - %s", paths); - handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); - } - break; - case OBSERVE_CANCEL: - case OBSERVE_CANCEL_ALL: - int observeCancelCnt = 0; - String observeCancelMsg = null; - if (OBSERVE_CANCEL.name().equals(typeOper)) { - observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); - observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, - OBSERVE_CANCEL.name(), target, observeCancelCnt); - } else { - observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); - observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, - OBSERVE_CANCEL.name(), observeCancelCnt); - } - this.afterObserveCancel(client, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); - break; - // lwm2mClientRpcRequest != null - case FW_UPDATE: - handler.getInfoFirmwareUpdate(client, lwm2mClientRpcRequest); - break; - } - } - } catch (Exception e) { - String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, - typeOper.name(), e.getMessage()); - handler.sendLogsToThingsboard(client, msg); - if (lwm2mClientRpcRequest != null) { - String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); - } - } - } - - private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient client, LwM2mTypeOper typeOper, - ContentFormat contentFormat, String target, String targetIdVer, - LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { - SimpleDownlinkRequest request = null; - switch (typeOper) { - case READ: - request = new ReadRequest(contentFormat, target); - break; - case DISCOVER: - request = new DiscoverRequest(target); - break; - case OBSERVE: - String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer); - log.warn(msg); - if (resultIds.isResource()) { - Set observations = context.getServer().getObservationService().getObservations(registration); - Set paths = observations.stream().filter(observation -> observation.getPath().equals(resultIds)).collect(Collectors.toSet()); - if (paths.size() == 0) { - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); - } else { - request = new ReadRequest(contentFormat, target); - } - } else if (resultIds.isObjectInstance()) { - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); - } else if (resultIds.getObjectId() >= 0) { - request = new ObserveRequest(contentFormat, resultIds.getObjectId()); - } - break; - case EXECUTE: - ResourceModel resourceModelExecute = client.getResourceModel(targetIdVer, this.config.getModelProvider()); - if (resourceModelExecute != null) { - if (params != null && !resourceModelExecute.multiple) { - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds)); - } else { - request = new ExecuteRequest(target); - } - } - break; - case WRITE_REPLACE: - /** - * Request to write a String Single-Instance Resource using the TLV content format. - * Type from resourceModel -> STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK - * contentFormat -> TLV, TLV, TLV, TLV, OPAQUE, TLV, LINK - * JSON, TEXT; - **/ - ResourceModel resourceModelWrite = client.getResourceModel(targetIdVer, this.config.getModelProvider()); - if (resourceModelWrite != null) { -// contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); -// request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), -// resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, -// client, rpcRequest); - } - break; - case WRITE_UPDATE: - if (resultIds.isResource()) { - /** - * send request: path = '/3/0' node == wM2mObjectInstance - * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} - **/ - Collection resources = client.getNewResourceForInstance( - targetIdVer, params, - this.config.getModelProvider(), - this.converter); -// contentFormat = getContentFormatByResourceModelType(client.getResourceModel(targetIdVer, this.config.getModelProvider()), -// contentFormat); -// request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), -// resultIds.getObjectInstanceId(), resources); - } - /** - * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" - * int rscId = resultIds.getObjectInstanceId(); - * contentFormat – Format of the payload (TLV or JSON). - */ - else if (resultIds.isObjectInstance()) { - if (((ConcurrentHashMap) params).size() > 0) { - Collection resources = client.getNewResourcesForInstance( - targetIdVer, params, - this.config.getModelProvider(), - this.converter); - if (resources.size() > 0) { - contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : ContentFormat.TLV; - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), - resultIds.getObjectInstanceId(), resources); - } - } - } else if (resultIds.getObjectId() >= 0) { - request = new ObserveRequest(resultIds.getObjectId()); - } - break; - case WRITE_ATTRIBUTES: - request = createWriteAttributeRequest(target, params, this.handler); - break; - case DELETE: - request = new DeleteRequest(target); - break; - } - return request; } - private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs) { - try { - sendRequest(client, request, timeoutInMs, null); - } catch (ClientSleepingException e) { - //TODO: this may cause infinite loop / memory leak. - client.getQueuedRequests().add(() -> sendRequest(client, request, timeoutInMs, null)); - } - } +// public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, +// ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { +// Registration registration = client.getRegistration(); +// try { +// String target = fromVersionedIdToObjectId(targetIdVer); +// if (contentFormat == null) { +// contentFormat = client.getDefaultContentFormat(); +// } +// LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; +// if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0) { +// if (client.isValidObjectVersion(targetIdVer)) { +// timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; +// SimpleDownlinkRequest request = createRequest(registration, client, typeOper, contentFormat, target, +// targetIdVer, resultIds, params, lwm2mClientRpcRequest); +// if (request != null) { +// try { +// this.sendRequest(client, request, timeoutInMs, lwm2mClientRpcRequest); +// } catch (ClientSleepingException e) { +// SimpleDownlinkRequest finalRequest = request; +// long finalTimeoutInMs = timeoutInMs; +// LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; +// client.getQueuedRequests().add(() -> sendRequest(client, finalRequest, finalTimeoutInMs, finalRpcRequest)); +// } catch (Exception e) { +// log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); +// } +// } else if (WRITE_UPDATE.name().equals(typeOper.name())) { +// if (lwm2mClientRpcRequest != null) { +// String errorMsg = String.format("Path %s params is not valid", targetIdVer); +// handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { +// if (lwm2mClientRpcRequest != null) { +// String errorMsg = String.format("Path %s object model is absent", targetIdVer); +// handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { +// log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); +// if (lwm2mClientRpcRequest != null) { +// ResourceModel resourceModel = client.getResourceModel(targetIdVer, this.config.getModelProvider()); +// String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; +// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// } +// } else if (lwm2mClientRpcRequest != null) { +// String errorMsg = String.format("Path %s not found in object version", targetIdVer); +// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// } else { +// switch (typeOper) { +// case OBSERVE_READ_ALL: +// case DISCOVER_ALL: +// Set paths; +// if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { +// Set observations = context.getServer().getObservationService().getObservations(registration); +// paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); +// } else { +// assert registration != null; +// Link[] objectLinks = registration.getSortedObjectLinks(); +// paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); +// } +// String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, +// typeOper.name(), paths); +// this.handler.sendLogsToThingsboard(client, msg); +// if (lwm2mClientRpcRequest != null) { +// String valueMsg = String.format("Paths - %s", paths); +// handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); +// } +// break; +// case OBSERVE_CANCEL: +// case OBSERVE_CANCEL_ALL: +// int observeCancelCnt = 0; +// String observeCancelMsg = null; +// if (OBSERVE_CANCEL.name().equals(typeOper)) { +// observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); +// observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, +// OBSERVE_CANCEL.name(), target, observeCancelCnt); +// } else { +// observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); +// observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, +// OBSERVE_CANCEL.name(), observeCancelCnt); +// } +// this.afterObserveCancel(client, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); +// break; +// // lwm2mClientRpcRequest != null +// case FW_UPDATE: +// handler.getInfoFirmwareUpdate(client, lwm2mClientRpcRequest); +// break; +// } +// } +// } catch (Exception e) { +// String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, +// typeOper.name(), e.getMessage()); +// handler.sendLogsToThingsboard(client, msg); +// if (lwm2mClientRpcRequest != null) { +// String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); +// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); +// } +// } +// } - @SuppressWarnings({"error sendRequest"}) - private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { + private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs, DownlinkRequestCallback callback) { Registration registration = client.getRegistration(); - context.getServer().send(registration, request, timeoutInMs, (ResponseCallback) response -> { - - if (!client.isInit()) { - client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); - } - if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { - this.handleResponse(client, request.getPath().toString(), response, request, rpcRequest); - } else { - String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), - ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); - handler.sendLogsToThingsboard(client, msg); - log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), - ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); - if (!client.isInit()) { - client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); - } - /** Not Found */ - if (rpcRequest != null) { - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); - } - /** Not Found - set setClient_fw_info... = empty - **/ - if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { - client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); - } - if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { - client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); - } - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { - this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); - } - if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { - this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage()); + context.getServer().send(registration, request, timeoutInMs, response -> { +// if (!client.isInit()) { +// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); +// } + responseRequestExecutor.submit(() -> { + try { + callback.onSuccess(response); + } catch (Exception e) { + log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); } - } + }); +// if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { +// this.handleResponse(client, request.getPath().toString(), response, request, rpcRequest); +// } else { +// String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), +// ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); +// handler.sendLogsToThingsboard(client, msg); +// log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), +// ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); +// if (!client.isInit()) { +// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); +// } +// /** Not Found */ +// if (rpcRequest != null) { +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); +// } +// /** Not Found +// set setClient_fw_info... = empty +// **/ +// if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { +// client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); +// } +// if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { +// client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); +// } +// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { +// this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); +// } +// if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { +// this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage()); +// } +// } }, e -> { - /** version == null - set setClient_fw_info... = empty - **/ - if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { - client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); - } - if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { - client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); - } - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { - this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); - } - if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { - this.afterExecuteFwSwUpdateError(registration, request, e.getMessage()); - } - if (!client.isInit()) { - client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); - } - String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", - LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); - handler.sendLogsToThingsboard(client, msg); - log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); - if (rpcRequest != null) { - handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); - } + responseRequestExecutor.submit(() -> { + callback.onError(e); + }); +// /** version == null +// set setClient_fw_info... = empty +// **/ +// if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { +// client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); +// } +// if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { +// client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); +// } +// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { +// this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); +// } +// if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { +// this.afterExecuteFwSwUpdateError(registration, request, e.getMessage()); +// } +// if (!client.isInit()) { +// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); +// } +// String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", +// LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); +// handler.sendLogsToThingsboard(client, msg); +// log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); +// if (rpcRequest != null) { +// handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); +// } }); } @@ -622,7 +459,9 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } return new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); default: + throw new IllegalArgumentException("Not supported type:" + type.name()); } + // TODO: throw exception and execute callback. //// if (rpcRequest != null) { //// String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; @@ -663,158 +502,193 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { */ private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response, SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { - Registration registration = lwM2mClient.getRegistration(); - String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); - String msgLog = ""; - if (response instanceof ReadResponse) { - handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); - } else if (response instanceof DeleteResponse) { - log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response); - if (rpcRequest != null) { - rpcRequest.setInfoMsg(null); - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null); - } - } else if (response instanceof DiscoverResponse) { - String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); - msgLog = String.format("%s: type operation: %s path: %s value: %s", - LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); - handler.sendLogsToThingsboard(lwM2mClient, msgLog); - log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); - if (rpcRequest != null) { - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); - } - } else if (response instanceof ExecuteResponse) { - msgLog = String.format("%s: type operation: %s path: %s", - LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); - log.warn("9) [{}] ", msgLog); - handler.sendLogsToThingsboard(lwM2mClient, msgLog); - if (rpcRequest != null) { - msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); - rpcRequest.setInfoMsg(msgLog); - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO); - } - - } else if (response instanceof WriteAttributesResponse) { - msgLog = String.format("%s: type operation: %s path: %s value: %s", - LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); - handler.sendLogsToThingsboard(lwM2mClient, msgLog); - log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); - if (rpcRequest != null) { - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); - } - } else if (response instanceof WriteResponse) { - msgLog = String.format("Type operation: Write path: %s", pathIdVer); - log.warn("10) [{}] response: [{}]", msgLog, response); - this.infoWriteResponse(lwM2mClient, response, request, rpcRequest); - handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); - } +// Registration registration = lwM2mClient.getRegistration(); +// String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); +// String msgLog = ""; +// if (response instanceof ReadResponse) { +// handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); +// } else if (response instanceof DeleteResponse) { +// log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response); +// if (rpcRequest != null) { +// rpcRequest.setInfoMsg(null); +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null); +// } +// } else if (response instanceof DiscoverResponse) { +// String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); +// msgLog = String.format("%s: type operation: %s path: %s value: %s", +// LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); +// handler.sendLogsToThingsboard(lwM2mClient, msgLog); +// log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); +// if (rpcRequest != null) { +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); +// } +// } else if (response instanceof ExecuteResponse) { +// msgLog = String.format("%s: type operation: %s path: %s", +// LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); +// log.warn("9) [{}] ", msgLog); +// handler.sendLogsToThingsboard(lwM2mClient, msgLog); +// if (rpcRequest != null) { +// msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); +// rpcRequest.setInfoMsg(msgLog); +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO); +// } +// +// } else if (response instanceof WriteAttributesResponse) { +// msgLog = String.format("%s: type operation: %s path: %s value: %s", +// LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); +// handler.sendLogsToThingsboard(lwM2mClient, msgLog); +// log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); +// if (rpcRequest != null) { +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); +// } +// } else if (response instanceof WriteResponse) { +// msgLog = String.format("Type operation: Write path: %s", pathIdVer); +// log.warn("10) [{}] response: [{}]", msgLog, response); +// this.infoWriteResponse(lwM2mClient, response, request, rpcRequest); +// handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); +// } } - private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest - request, LwM2mClientRpcRequest rpcRequest) { - try { - Registration registration = lwM2mClient.getRegistration(); - LwM2mNode node = ((WriteRequest) request).getNode(); - String msg = null; - Object value; - if (node instanceof LwM2mObject) { - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString()); - } else if (node instanceof LwM2mObjectInstance) { - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint()); - } else if (node instanceof LwM2mSingleResource) { - LwM2mSingleResource singleResource = (LwM2mSingleResource) node; - if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { - int valueLength; - if (singleResource.getType() == ResourceModel.Type.STRING) { - valueLength = ((String) singleResource.getValue()).length(); - value = ((String) singleResource.getValue()) - .substring(Math.min(valueLength, config.getLogMaxLength())).trim(); - - } else { - valueLength = ((byte[]) singleResource.getValue()).length; - value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), - Math.min(valueLength, config.getLogMaxLength()))).trim(); - } - value = valueLength > config.getLogMaxLength() ? value + "..." : value; - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); - } else { - value = this.converter.convertValue(singleResource.getValue(), - singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); - msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); - } - } - if (msg != null) { - handler.sendLogsToThingsboard(lwM2mClient, msg); - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { - this.afterWriteSuccessFwSwUpdate(registration, request); - if (rpcRequest != null) { - rpcRequest.setInfoMsg(msg); - } - } else if (rpcRequest != null) { - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); - } - } - } catch (Exception e) { - log.trace("Fail convert value from request to string. ", e); - } - } +// private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest +// request, LwM2mClientRpcRequest rpcRequest) { +// try { +// Registration registration = lwM2mClient.getRegistration(); +// LwM2mNode node = ((WriteRequest) request).getNode(); +// String msg = null; +// Object value; +// if (node instanceof LwM2mObject) { +// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", +// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString()); +// } else if (node instanceof LwM2mObjectInstance) { +// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", +// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint()); +// } else if (node instanceof LwM2mSingleResource) { +// LwM2mSingleResource singleResource = (LwM2mSingleResource) node; +// if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { +// int valueLength; +// if (singleResource.getType() == ResourceModel.Type.STRING) { +// valueLength = ((String) singleResource.getValue()).length(); +// value = ((String) singleResource.getValue()) +// .substring(Math.min(valueLength, config.getLogMaxLength())).trim(); +// +// } else { +// valueLength = ((byte[]) singleResource.getValue()).length; +// value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), +// Math.min(valueLength, config.getLogMaxLength()))).trim(); +// } +// value = valueLength > config.getLogMaxLength() ? value + "..." : value; +// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", +// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); +// } else { +// value = this.converter.convertValue(singleResource.getValue(), +// singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); +// msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", +// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); +// } +// } +// if (msg != null) { +// handler.sendLogsToThingsboard(lwM2mClient, msg); +// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { +// this.afterWriteSuccessFwSwUpdate(registration, request); +// if (rpcRequest != null) { +// rpcRequest.setInfoMsg(msg); +// } +// } else if (rpcRequest != null) { +// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); +// } +// } +// } catch (Exception e) { +// log.trace("Fail convert value from request to string. ", e); +// } +// } /** * After finish operation FwSwUpdate Write (success): * fw_state/sw_state = DOWNLOADED * send operation Execute */ - private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) { - LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { - client.getFwUpdate().setStateUpdate(DOWNLOADED.name()); - client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); - } - if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { - client.getSwUpdate().setStateUpdate(DOWNLOADED.name()); - client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); - } - } +// private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) { +// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); +// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { +// client.getFwUpdate().setStateUpdate(DOWNLOADED.name()); +// client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); +// } +// if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { +// client.getSwUpdate().setStateUpdate(DOWNLOADED.name()); +// client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); +// } +// } /** * After finish operation FwSwUpdate Write (error): fw_state = FAILED */ - private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String - msgError) { - LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { - client.getFwUpdate().setStateUpdate(FAILED.name()); - client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); - } - if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { - client.getSwUpdate().setStateUpdate(FAILED.name()); - client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); - } +// private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String +// msgError) { +// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); +// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { +// client.getFwUpdate().setStateUpdate(FAILED.name()); +// client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); +// } +// if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { +// client.getSwUpdate().setStateUpdate(FAILED.name()); +// client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); +// } +// } + +// private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String +// msgError) { +// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); +// if (request.getPath().toString().equals(FW_UPDATE_ID) && client.getFwUpdate() != null) { +// client.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); +// } +// if (request.getPath().toString().equals(SW_INSTALL_ID) && client.getSwUpdate() != null) { +// client.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); +// } +// } + +// private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String +// observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { +// handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg); +// log.warn("[{}]", observeCancelMsg); +// if (rpcRequest != null) { +// rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); +// handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); +// } +// } + + + private static void addAttribute(List attributes, String attributeName, T value) { + addAttribute(attributes, attributeName, value, null, null); } - private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String - msgError) { - LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_UPDATE_ID) && client.getFwUpdate() != null) { - client.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); - } - if (request.getPath().toString().equals(SW_INSTALL_ID) && client.getSwUpdate() != null) { - client.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); + private static void addAttribute(List attributes, String attributeName, T value, Function converter) { + addAttribute(attributes, attributeName, value, null, converter); + } + + private static void addAttribute(List attributes, String attributeName, T value, Predicate filter, Function converter) { + if (value != null && ((filter == null) || filter.test(value))) { + attributes.add(new Attribute(attributeName, converter != null ? converter.apply(value) : value)); } } - private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String - observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { - handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg); - log.warn("[{}]", observeCancelMsg); - if (rpcRequest != null) { - rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); - handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); + private static ContentFormat convertResourceModelTypeToContentFormat(LwM2mClient client, ResourceModel.Type type) { + switch (type) { + case BOOLEAN: + case STRING: + case TIME: + case INTEGER: + case FLOAT: + return client.getDefaultContentFormat(); + case OPAQUE: + return ContentFormat.OPAQUE; + case OBJLNK: + return ContentFormat.LINK; + default: } + throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); } + private static ContentFormat getContentFormat(LwM2mClient client, HasContentFormat request) { + return request.getContentFormat() != null ? request.getContentFormat() : client.getDefaultContentFormat(); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java index 40ad57e026..ed6226fc1d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java @@ -15,34 +15,56 @@ */ package org.thingsboard.server.transport.lwm2m.server; -import org.eclipse.leshan.core.request.ContentFormat; -import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.eclipse.leshan.core.response.CancelObservationResponse; +import org.eclipse.leshan.core.response.DeleteResponse; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.ExecuteResponse; +import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; + +import java.util.Set; public interface LwM2mDownlinkMsgHandler { - void sendReadRequest(LwM2mClient client, String targetId, Long timeout); + void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback); + + void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback); + + void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback); - void sendReadRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout); + void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback); - void sendObserveRequest(LwM2mClient client, String targetId, Long timeout); + void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback); - void sendObserveRequest(LwM2mClient client, String targetId, ContentFormat contentFormat, Long timeout); + void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback); - void sendExecuteRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback); + void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback); - void sendExecuteRequest(LwM2mClient client, String targetId, Object params, Long timeout, DownlinkRequestCallback callback); + void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback); - void sendCancelObserveRequest(LwM2mClient client, String targetId, Long timeout, DownlinkRequestCallback callback); + void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); - void sendCancelAllRequest(LwM2mClient client, Long timeout, DownlinkRequestCallback callback); + void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback); - void sendDiscoverRequest(LwM2mClient client, String targetId, Long timeout); + void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback); - void sendWriteAttributesRequest(LwM2mClient client, String targetId, ObjectAttributes params, Long timeout); + void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback); - void sendWriteReplaceRequest(LwM2mClient client, String targetIdVer, Object newValue, Long timeout, DownlinkRequestCallback callback); - void sendWriteUpdateRequest(LwM2mClient client, String targetIdVer, Object newValue, ContentFormat contentFormat, Long timeout, DownlinkRequestCallback callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java index fcccf29f1d..132bd8689d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java @@ -38,8 +38,6 @@ public interface LwM2mUplinkMsgHandler { void onSleepingDev(Registration registration); - void setCancelObservationsAll(Registration registration); - void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest); void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 5c210d2af3..9148a44e93 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -39,6 +39,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import java.util.Collection; @@ -394,18 +395,18 @@ public class LwM2mClient implements Cloneable { } } - public LwM2mFwSwUpdate getFwUpdate (LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { if (this.fwUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); - this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); + this.fwUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); } return this.fwUpdate; } - public LwM2mFwSwUpdate getSwUpdate (LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getSwUpdate (LwM2mUplinkMsgHandler handler,LwM2mClientContext clientContext) { if (this.swUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); - this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); + this.swUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); } return this.fwUpdate; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index efc5aa9657..0dda5d7269 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -27,6 +27,13 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import java.util.ArrayList; import java.util.List; @@ -112,6 +119,9 @@ public class LwM2mFwSwUpdate { @Setter private volatile boolean infoFwSwUpdate = false; private final OtaPackageType type; + + private final LwM2mUplinkMsgHandler handler; + @Getter LwM2mClient lwM2MClient; @Getter @@ -124,7 +134,8 @@ public class LwM2mFwSwUpdate { @Setter private volatile int updateStrategy; - public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { + public LwM2mFwSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { + this.handler = handler; this.lwM2MClient = lwM2MClient; this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.type = type; @@ -189,7 +200,9 @@ public class LwM2mFwSwUpdate { int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); - request.sendWriteReplaceRequest(this.lwM2MClient, targetIdVer, firmwareChunk, handler.config.getTimeout(), this.rpcRequest); + + TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { Registration registration = this.getLwM2MClient().getRegistration(); // String api = handler.config.getHostRequests(); @@ -197,7 +210,9 @@ public class LwM2mFwSwUpdate { int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); - request.sendWriteReplaceRequest(this.lwM2MClient, targetIdVer, uri, handler.config.getTimeout(), this.rpcRequest); + //TODO: user this.rpcRequest??? + TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } @@ -230,7 +245,9 @@ public class LwM2mFwSwUpdate { */ public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); - request.sendExecuteRequest(this.lwM2MClient, this.pathInstallId, handler.config.getTimeout(), this.rpcRequest); + //TODO: user this.rpcRequest??? + TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build(); + request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteRequestCallback(handler, lwM2MClient, pathInstallId)); } /** @@ -253,23 +270,21 @@ public class LwM2mFwSwUpdate { String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID); // #1/#2 String fwMsg = null; - if ((this.currentVersion != null && ( + if ((this.currentVersion != null && ( ver5 != null && ver5.equals(this.currentVersion) || - ver3 != null && ver3.contains(this.currentVersion) - )) || - (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { + ver3 != null && ver3.contains(this.currentVersion) + )) || + (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LW2M_ERROR, this.currentVersion); - } - else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LW2M_ERROR, updateResultFw); } if (fwMsg != null) { handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); return false; - } - else { + } else { return true; } } @@ -415,8 +430,9 @@ public class LwM2mFwSwUpdate { this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pathNameId, this.lwM2MClient.getRegistration())); } - this.pendingInfoRequestsStart.forEach(pathIdVer -> { - request.sendObserveRequest(this.lwM2MClient, pathIdVer, 0, this.rpcRequest); + this.pendingInfoRequestsStart.forEach(versionedId -> { + TbLwM2MObserveRequest downlink = TbLwM2MObserveRequest.builder().versionedId(versionedId).build(); + request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveRequestCallback(handler, lwM2MClient, versionedId)); }); } @@ -449,7 +465,7 @@ public class LwM2mFwSwUpdate { } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); - if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { + if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { this.stateUpdate = state; this.sendSateOnThingsBoard(handler); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java new file mode 100644 index 0000000000..16723aaadf --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public abstract class AbstractTbLwM2MRequestCallback implements DownlinkRequestCallback { + + protected final LwM2mUplinkMsgHandler handler; + protected final LwM2mClient client; + + protected AbstractTbLwM2MRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { + this.handler = handler; + this.client = client; + } + + @Override + public void onValidationError(String msg) { + + } + + @Override + public void onError(Exception e) { + + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MTargetedDownlinkRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MTargetedDownlinkRequest.java new file mode 100644 index 0000000000..7d81308df1 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MTargetedDownlinkRequest.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Getter; + +public abstract class AbstractTbLwM2MTargetedDownlinkRequest implements TbLwM2MDownlinkRequest, HasVersionedId { + + @Getter + private final String versionedId; + @Getter + private final long timeout; + + public AbstractTbLwM2MTargetedDownlinkRequest(String versionedId, long timeout) { + this.versionedId = versionedId; + this.timeout = timeout; + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java index 84f1854e1d..342abf39f4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java @@ -15,11 +15,30 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +public interface DownlinkRequestCallback { -public interface DownlinkRequestCallback { + void onSuccess(T response); - void onSuccess(LwM2mClient client, String msg); + void onValidationError(String msg); - void onError(LwM2mClient client, String msg, Exception e); + void onError(Exception e); + + static DownlinkRequestCallback doNothing() { + return new DownlinkRequestCallback() { + @Override + public void onSuccess(T response) { + + } + + @Override + public void onValidationError(String msg) { + + } + + @Override + public void onError(Exception e) { + + } + }; + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasContentFormat.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasContentFormat.java new file mode 100644 index 0000000000..6d2ecc98ed --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasContentFormat.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.request.ContentFormat; + +public interface HasContentFormat { + + ContentFormat getContentFormat(); +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java new file mode 100644 index 0000000000..fcbb195935 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/HasVersionedId.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public interface HasVersionedId { + + String getVersionedId(); + + default String getObjectId(){ + return LwM2mTransportUtil.fromVersionedIdToObjectId(getVersionedId()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java new file mode 100644 index 0000000000..a76f317085 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; + +public class TbLwM2MCancelAllObserveRequestCallback extends AbstractTbLwM2MRequestCallback { + + public TbLwM2MCancelAllObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { + super(handler, client); + } + + @Override + public void onSuccess(Integer canceledSubscriptionsCount) { + String observeCancelMsg = String.format("%s: type operation %s paths: count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL_ALL.name(), canceledSubscriptionsCount); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java new file mode 100644 index 0000000000..b6f796aefd --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.response.CancelObservationResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MCancelAllRequest implements TbLwM2MDownlinkRequest { + + @Getter + private final long timeout; + + @Builder + private TbLwM2MCancelAllRequest(long timeout) { + this.timeout = timeout; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java new file mode 100644 index 0000000000..51b016f80d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import org.eclipse.leshan.core.response.CancelObservationResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MCancelObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Builder + private TbLwM2MCancelObserveRequest(String versionedId, long timeout) { + super(versionedId, timeout); + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java new file mode 100644 index 0000000000..48d7519006 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; + +public class TbLwM2MCancelObserveRequestCallback extends AbstractTbLwM2MRequestCallback { + + private final String versionedId; + + public TbLwM2MCancelObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { + super(handler, client); + this.versionedId = versionedId; + } + + @Override + public void onSuccess(Integer canceledSubscriptionsCount) { + String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), versionedId, canceledSubscriptionsCount); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java new file mode 100644 index 0000000000..63a360b822 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MDeleteRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Builder + private TbLwM2MDeleteRequest(String versionedId, long timeout) { + super(versionedId, timeout); + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.DELETE; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java new file mode 100644 index 0000000000..106f34a14d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.DeleteResponse; +import org.eclipse.leshan.core.response.ExecuteResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MDeleteRequestCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MDeleteRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(DeleteResponse response) { + //TODO: separate callback wrapper for the RPC calls. + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java new file mode 100644 index 0000000000..16f32a9ecc --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MDiscoverAllRequest implements TbLwM2MDownlinkRequest { + + @Getter + private final long timeout; + + @Builder + private TbLwM2MDiscoverAllRequest(long timeout) { + this.timeout = timeout; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java new file mode 100644 index 0000000000..fa0e3cf761 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.DeleteResponse; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MDiscoverCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MDiscoverCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(DiscoverResponse response) { + //TODO: separate callback wrapper for the RPC calls. + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java new file mode 100644 index 0000000000..6b0dcc4994 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MDiscoverRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Builder + private TbLwM2MDiscoverRequest(String versionedId, long timeout) { + super(versionedId, timeout); + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java new file mode 100644 index 0000000000..a069bb4ddc --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public interface TbLwM2MDownlinkRequest { + + LwM2mTransportUtil.LwM2mTypeOper getType(); + + long getTimeout(); + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java new file mode 100644 index 0000000000..32d1be665c --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MExecuteRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Getter + private final Object params; + + @Builder + private TbLwM2MExecuteRequest(String versionedId, long timeout, Object params) { + super(versionedId, timeout); + this.params = params; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java new file mode 100644 index 0000000000..4a2ca7f1fb --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.ExecuteResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MExecuteRequestCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MExecuteRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(ExecuteResponse response) { + //TODO: separate callback wrapper for the RPC calls. + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java new file mode 100644 index 0000000000..b12b4469d6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +import java.util.Set; + +public class TbLwM2MObserveAllRequest implements TbLwM2MDownlinkRequest> { + + @Getter + private final long timeout; + + @Builder + private TbLwM2MObserveAllRequest(long timeout) { + this.timeout = timeout; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java new file mode 100644 index 0000000000..1239725be2 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest implements HasContentFormat { + + @Getter + private final ContentFormat contentFormat; + + @Builder + private TbLwM2MObserveRequest(String versionedId, long timeout, ContentFormat contentFormat) { + super(versionedId, timeout); + this.contentFormat = contentFormat; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java new file mode 100644 index 0000000000..65f04cd920 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MObserveRequestCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(ObserveResponse response) { + //TODO: handle the response (at least log to telemetry) + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java new file mode 100644 index 0000000000..5d88a20ae4 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MReadRequest extends AbstractTbLwM2MTargetedDownlinkRequest implements HasContentFormat { + + @Getter + private final ContentFormat contentFormat; + + @Builder + private TbLwM2MReadRequest(String versionedId, long timeout, ContentFormat contentFormat) { + super(versionedId, timeout); + this.contentFormat = contentFormat; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.READ; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java new file mode 100644 index 0000000000..345c585b1f --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MReadRequestCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MReadRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(ReadResponse response) { + //TODO: separate callback wrapper for the RPC calls. + handler.onUpdateValueAfterReadResponse(client.getRegistration(), targetId, response, null); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java new file mode 100644 index 0000000000..4bda629256 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.ReadResponse; +import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.eclipse.leshan.core.response.WriteResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MWriteAttributesCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MWriteAttributesCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(WriteAttributesResponse response) { + //TODO: separate callback wrapper for the RPC calls. + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java new file mode 100644 index 0000000000..dd1f9311d9 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.response.ObserveResponse; +import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MWriteAttributesRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Getter + private final ObjectAttributes attributes; + + @Builder + private TbLwM2MWriteAttributesRequest(String versionedId, long timeout, ObjectAttributes attributes) { + super(versionedId, timeout); + this.attributes = attributes; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java new file mode 100644 index 0000000000..b15632da19 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.eclipse.leshan.core.response.WriteResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public class TbLwM2MWriteReplaceCallback extends AbstractTbLwM2MRequestCallback { + + private final String targetId; + + public TbLwM2MWriteReplaceCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(WriteResponse response) { + //TODO: separate callback wrapper for the RPC calls. + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java new file mode 100644 index 0000000000..71c1080655 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.eclipse.leshan.core.response.WriteResponse; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Getter + private final Object value; + + @Builder + private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, Object value) { + super(versionedId, timeout); + this.value = value; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; + } + + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java new file mode 100644 index 0000000000..071d9fc889 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.Builder; +import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.response.WriteResponse; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; + +public class TbLwM2MWriteUpdateRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + + @Getter + private final Object value; + @Getter + private final ContentFormat objectContentFormat; + + @Builder + private TbLwM2MWriteUpdateRequest(String versionedId, long timeout, Object value, ContentFormat objectContentFormat) { + super(versionedId, timeout); + this.value = value; + this.objectContentFormat = objectContentFormat; + } + + @Override + public LwM2mTransportUtil.LwM2mTypeOper getType() { + return LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; + } + + + +} From 26b8a7ff3aae22f24be17a9622f3e83b4451c4b5 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 11 Jun 2021 12:35:57 +0300 Subject: [PATCH 035/151] Rename of the callbacks --- .../server/DefaultLwM2MUplinkMsgHandler.java | 18 +++++++----------- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 12 +++++------- .../DefaultLwM2mDownlinkMsgHandler.java | 17 ++--------------- .../LwM2mDownlinkMsgHandler.java | 16 +--------------- ...va => TbLwM2MCancelAllObserveCallback.java} | 5 ++--- .../downlink/TbLwM2MCancelAllRequest.java | 1 - ....java => TbLwM2MCancelObserveCallback.java} | 4 ++-- .../downlink/TbLwM2MCancelObserveRequest.java | 2 -- ...allback.java => TbLwM2MDeleteCallback.java} | 5 ++--- .../server/downlink/TbLwM2MDeleteRequest.java | 1 - .../downlink/TbLwM2MDiscoverAllRequest.java | 1 - .../downlink/TbLwM2MDiscoverCallback.java | 1 - .../downlink/TbLwM2MDiscoverRequest.java | 1 - ...llback.java => TbLwM2MExecuteCallback.java} | 5 ++--- ...llback.java => TbLwM2MObserveCallback.java} | 5 ++--- .../server/downlink/TbLwM2MObserveRequest.java | 1 - ...tCallback.java => TbLwM2MReadCallback.java} | 4 ++-- .../TbLwM2MWriteAttributesCallback.java | 2 -- .../TbLwM2MWriteAttributesRequest.java | 2 -- .../downlink/TbLwM2MWriteReplaceCallback.java | 1 - .../downlink/TbLwM2MWriteReplaceRequest.java | 2 -- 21 files changed, 27 insertions(+), 79 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => downlink}/DefaultLwM2mDownlinkMsgHandler.java (97%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => downlink}/LwM2mDownlinkMsgHandler.java (68%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MCancelAllObserveRequestCallback.java => TbLwM2MCancelAllObserveCallback.java} (80%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MCancelObserveRequestCallback.java => TbLwM2MCancelObserveCallback.java} (86%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MDeleteRequestCallback.java => TbLwM2MDeleteCallback.java} (81%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MExecuteRequestCallback.java => TbLwM2MExecuteCallback.java} (81%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MObserveRequestCallback.java => TbLwM2MObserveCallback.java} (81%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MReadRequestCallback.java => TbLwM2MReadCallback.java} (85%) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java index e6c14b0ba0..55129e59c9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java @@ -29,7 +29,6 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.observation.Observation; -import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; @@ -67,18 +66,15 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; -import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllObserveRequestCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; @@ -659,12 +655,12 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, new TbLwM2MReadRequestCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)); } private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, new TbLwM2MObserveRequestCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, new TbLwM2MObserveCallback(this, lwM2MClient, versionedId)); } private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { @@ -674,7 +670,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void sendCancelObserveRequest(String versionedId, LwM2mClient client) { TbLwM2MCancelObserveRequest request = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveRequestCallback(this, client, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(this, client, versionedId)); } /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 0dda5d7269..7a7ca21a0f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -19,19 +19,18 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; @@ -67,7 +66,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; @@ -247,7 +245,7 @@ public class LwM2mFwSwUpdate { this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); //TODO: user this.rpcRequest??? TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build(); - request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteRequestCallback(handler, lwM2MClient, pathInstallId)); + request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteCallback(handler, lwM2MClient, pathInstallId)); } /** @@ -432,7 +430,7 @@ public class LwM2mFwSwUpdate { } this.pendingInfoRequestsStart.forEach(versionedId -> { TbLwM2MObserveRequest downlink = TbLwM2MObserveRequest.builder().versionedId(versionedId).build(); - request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveRequestCallback(handler, lwM2MClient, versionedId)); + request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveCallback(handler, lwM2MClient, versionedId)); }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java similarity index 97% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index b1b52ec3ed..189d359762 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -50,23 +50,10 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.HasContentFormat; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java similarity index 68% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java index ed6226fc1d..473205eaa1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.eclipse.leshan.core.response.CancelObservationResponse; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ExecuteResponse; @@ -24,19 +23,6 @@ import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import java.util.Set; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java similarity index 80% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index a76f317085..87e219ef96 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -19,12 +19,11 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; -public class TbLwM2MCancelAllObserveRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { - public TbLwM2MCancelAllObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { + public TbLwM2MCancelAllObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { super(handler, client); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java index b6f796aefd..02ff01f973 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.eclipse.leshan.core.response.CancelObservationResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MCancelAllRequest implements TbLwM2MDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java similarity index 86% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index 48d7519006..8d9c13a428 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -21,11 +21,11 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -public class TbLwM2MCancelObserveRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { private final String versionedId; - public TbLwM2MCancelObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { + public TbLwM2MCancelObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { super(handler, client); this.versionedId = versionedId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java index 51b016f80d..bf1ba36216 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java @@ -16,8 +16,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; -import org.eclipse.leshan.core.response.CancelObservationResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MCancelObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java similarity index 81% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java index 106f34a14d..f6b0bfac81 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java @@ -16,15 +16,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.DeleteResponse; -import org.eclipse.leshan.core.response.ExecuteResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MDeleteRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MDeleteCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MDeleteRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MDeleteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java index 63a360b822..62532c1726 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; -import lombok.Getter; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java index 16f32a9ecc..9400e3bfba 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.eclipse.leshan.core.response.DiscoverResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MDiscoverAllRequest implements TbLwM2MDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java index fa0e3cf761..dc3269b5a8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java index 6b0dcc4994..6b8319bc25 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java @@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import org.eclipse.leshan.core.response.DiscoverResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MDiscoverRequest extends AbstractTbLwM2MTargetedDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java similarity index 81% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java index 4a2ca7f1fb..f43a648cc3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java @@ -16,15 +16,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.ExecuteResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MExecuteRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MExecuteRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MExecuteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java similarity index 81% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java index 65f04cd920..d57a348186 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java @@ -16,15 +16,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MObserveRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MObserveCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MObserveRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java index 1239725be2..60fb9468f3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java @@ -19,7 +19,6 @@ import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest implements HasContentFormat { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java similarity index 85% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java index 345c585b1f..f142d53c09 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java @@ -19,11 +19,11 @@ import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MReadRequestCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MReadCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MReadRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java index 4bda629256..89802c1bf1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.WriteAttributesResponse; -import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java index dd1f9311d9..baa7401742 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java @@ -17,8 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.eclipse.leshan.core.request.ContentFormat; -import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java index b15632da19..ecfa9241de 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java index 71c1080655..488942f7de 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java @@ -17,9 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; -import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest { From bafbf7b239386a86077f32abc67e33befc101a4c Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 11 Jun 2021 14:34:43 +0300 Subject: [PATCH 036/151] added rpc ttl --- .../service/ttl/rpc/RpcCleanUpService.java | 83 +++++++++++++++++++ .../src/main/resources/thingsboard.yml | 3 + .../DefaultTenantProfileConfiguration.java | 1 + .../thingsboard/server/dao/rpc/RpcDao.java | 3 + .../server/dao/sql/rpc/JpaRpcDao.java | 6 ++ .../server/dao/sql/rpc/RpcRepository.java | 6 ++ ...enant-profile-configuration.component.html | 12 +++ ...-tenant-profile-configuration.component.ts | 3 +- ui-ngx/src/app/shared/models/tenant.model.ts | 6 +- .../assets/locale/locale.constant-en_US.json | 3 + 10 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java new file mode 100644 index 0000000000..c0985eb4c1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ttl.rpc; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.dao.rpc.RpcDao; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.Date; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +@TbCoreComponent +@Service +@Slf4j +@RequiredArgsConstructor +public class RpcCleanUpService { + @Value("${sql.ttl.rpc.enabled}") + private boolean ttlTaskExecutionEnabled; + + private final TenantDao tenantDao; + private final PartitionService partitionService; + private final TbTenantProfileCache tenantProfileCache; + private final RpcDao rpcDao; + + @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.rpc.checking_interval})}", fixedDelayString = "${sql.ttl.rpc.checking_interval}") + public void cleanUp() { + if (ttlTaskExecutionEnabled) { + PageLink tenantsBatchRequest = new PageLink(10_000, 0); + PageData tenantsIds; + do { + tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); + for (TenantId tenantId : tenantsIds.getData()) { + if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { + continue; + } + + Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getRpcTtlDays() == 0) { + continue; + } + + long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays()); + long expirationTime = System.currentTimeMillis() - ttl; + + long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); + + if (totalRemoved > 0) { + log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); + } + } + + tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); + } while (tenantsIds.hasNext()); + } + } + +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 54721ba2fa..7fd0d47184 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -276,6 +276,9 @@ sql: alarms: checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches + rpc: + enabled: "${SQL_TTL_RPC_ENABLED:true}" + checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours # Actor system parameters actors: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 8cdccfe8bd..ce10f95055 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -56,6 +56,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private int defaultStorageTtlDays; private int alarmsTtlDays; + private int rpcTtlDays; private double warnThreshold; diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index 753d1043f3..a3aa32720a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.rpc; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; @@ -24,4 +25,6 @@ import org.thingsboard.server.dao.Dao; public interface RpcDao extends Dao { PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); + + Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 1a538c9d9d..22fc3396f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; @@ -53,4 +54,9 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao public PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { return DaoUtil.toPageData(rpcRepository.findAllByDeviceIdAndStatus(deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink))); } + + @Override + public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { + return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index bc04aa7e71..565d87e627 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -17,7 +17,9 @@ package org.thingsboard.server.dao.sql.rpc; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.dao.model.sql.RpcEntity; @@ -25,4 +27,8 @@ import java.util.UUID; public interface RpcRepository extends CrudRepository { Page findAllByDeviceIdAndStatus(UUID deviceId, RpcStatus status, Pageable pageable); + + @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted", + nativeQuery = true) + Long deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 4dd248a10c..66cb503bd3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -196,6 +196,18 @@ {{ 'tenant-profile.alarms-ttl-days-days-range' | translate}} + + tenant-profile.rpc-ttl-days + + + {{ 'tenant-profile.rpc-ttl-days-required' | translate}} + + + {{ 'tenant-profile.rpc-ttl-days-days-range' | translate}} + + tenant-profile.max-rule-node-executions-per-message { this.updateModel(); diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 4ce7acb5c5..5a3683f212 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -51,6 +51,8 @@ export interface DefaultTenantProfileConfiguration { maxSms: number; defaultStorageTtlDays: number; + alarmsTtlDays: number; + rpcTtlDays: number; } export type TenantProfileConfigurations = DefaultTenantProfileConfiguration; @@ -81,7 +83,9 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxRuleNodeExecutionsPerMessage: 0, maxEmails: 0, maxSms: 0, - defaultStorageTtlDays: 0 + defaultStorageTtlDays: 0, + alarmsTtlDays: 0, + rpcTtlDays: 0 }; configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT}; break; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index abe4cba802..7eb244ff4d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2570,6 +2570,9 @@ "alarms-ttl-days": "Alarms TTL days (0 - unlimited)", "alarms-ttl-days-required": "Alarms TTL days required", "alarms-ttl-days-days-range": "Alarms TTL days can't be negative", + "rpc-ttl-days": "RPC TTL days (0 - unlimited)", + "rpc-ttl-days-required": "RPC TTL days required", + "rpc-ttl-days-days-range": "RPC TTL days can't be negative", "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)", "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.", "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative", From 6b546a459ef29e7b0fcf8f0013e7f29865c49d73 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 14 Jun 2021 10:10:43 +0300 Subject: [PATCH 037/151] added RPC message types --- .../server/service/rpc/TbRpcService.java | 17 ++++++++++++----- .../server/common/data/DataConstants.java | 7 +++++++ .../rule/engine/filter/TbMsgTypeSwitchNode.java | 16 ++++++++++++++-- .../src/app/shared/models/rule-node.models.ts | 16 ++++++++++++++-- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index d85223c43a..d410f12b1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; @@ -27,9 +27,11 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.action.RuleEngineEntityActionService; +import org.thingsboard.server.service.queue.TbClusterService; @TbCoreComponent @Service @@ -37,11 +39,11 @@ import org.thingsboard.server.service.action.RuleEngineEntityActionService; @Slf4j public class TbRpcService { private final RpcService rpcService; - private final RuleEngineEntityActionService ruleEngineEntityActionService; + private final TbClusterService tbClusterService; public void save(TenantId tenantId, Rpc rpc) { Rpc saved = rpcService.save(tenantId, rpc); - ruleEngineEntityActionService.pushEntityActionToRuleEngine(saved.getId(), saved, tenantId, null, rpc.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + pushRpcMsgToRuleEngine(tenantId, saved); } public void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode response) { @@ -52,12 +54,17 @@ public class TbRpcService { foundRpc.setResponse(response); } Rpc saved = rpcService.save(tenantId, foundRpc); - ruleEngineEntityActionService.pushEntityActionToRuleEngine(saved.getId(), saved, tenantId, null, ActionType.UPDATED, null); + pushRpcMsgToRuleEngine(tenantId, saved); } else { log.warn("[{}] Failed to update RPC status because RPC was already deleted", rpcId); } } + private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { + TbMsg msg = TbMsg.newMsg("RPC_" + rpc.getStatus().name(), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getId(), msg, null); + } + public Rpc findRpcById(TenantId tenantId, RpcId rpcId) { return rpcService.findById(tenantId, rpcId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 002cbbb733..de97bf324c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -76,6 +76,13 @@ public class DataConstants { public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; + public static final String RPC_QUEUED = "RPC_QUEUED"; + public static final String RPC_SENT = "RPC_SENT"; + public static final String RPC_DELIVERED = "RPC_DELIVERED"; + public static final String RPC_SUCCESSFUL = "RPC_SUCCESSFUL"; + public static final String RPC_TIMEOUT = "RPC_TIMEOUT"; + public static final String RPC_FAILED = "RPC_FAILED"; + public static final String DEFAULT_SECRET_KEY = ""; public static final String SECRET_KEY_FIELD_NAME = "secretKey"; public static final String DURATION_MS_FIELD_NAME = "durationMs"; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index 7969836e00..235bdcfa44 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -33,8 +33,8 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; type = ComponentType.FILTER, name = "message type switch", configClazz = EmptyNodeConfiguration.class, - relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event", - "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", + relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "RPC Queued", "RPC Sent", "RPC Delivered", "RPC Successful", "RPC Timeout", "RPC Failed", + "Activity Event", "Inactivity Event", "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant", "Timeseries Updated", "Timeseries Deleted"}, nodeDescription = "Route incoming messages by Message Type", @@ -95,6 +95,18 @@ public class TbMsgTypeSwitchNode implements TbNode { relationType = "Timeseries Updated"; } else if (msg.getType().equals(DataConstants.TIMESERIES_DELETED)) { relationType = "Timeseries Deleted"; + } else if (msg.getType().equals(DataConstants.RPC_QUEUED)) { + relationType = "RPC Queued"; + } else if (msg.getType().equals(DataConstants.RPC_SENT)) { + relationType = "RPC Sent"; + } else if (msg.getType().equals(DataConstants.RPC_DELIVERED)) { + relationType = "RPC Delivered"; + } else if (msg.getType().equals(DataConstants.RPC_SUCCESSFUL)) { + relationType = "RPC Successful"; + } else if (msg.getType().equals(DataConstants.RPC_TIMEOUT)) { + relationType = "RPC Timeout"; + } else if (msg.getType().equals(DataConstants.RPC_FAILED)) { + relationType = "RPC Failed"; } else { relationType = "Other"; } diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index 73ff97b061..49c3f18bf8 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -352,7 +352,13 @@ export enum MessageType { ATTRIBUTES_UPDATED = 'ATTRIBUTES_UPDATED', ATTRIBUTES_DELETED = 'ATTRIBUTES_DELETED', TIMESERIES_UPDATED = 'TIMESERIES_UPDATED', - TIMESERIES_DELETED = 'TIMESERIES_DELETED' + TIMESERIES_DELETED = 'TIMESERIES_DELETED', + RPC_QUEUED = 'RPC_QUEUED', + RPC_SENT = 'RPC_SENT', + RPC_DELIVERED = 'RPC_SENT', + RPC_SUCCESSFUL = 'RPC_DELIVERED', + RPC_TIMEOUT = 'RPC_TIMEOUT', + RPC_FAILED = 'RPC_FAILED' } export const messageTypeNames = new Map( @@ -373,7 +379,13 @@ export const messageTypeNames = new Map( [MessageType.ATTRIBUTES_UPDATED, 'Attributes Updated'], [MessageType.ATTRIBUTES_DELETED, 'Attributes Deleted'], [MessageType.TIMESERIES_UPDATED, 'Timeseries Updated'], - [MessageType.TIMESERIES_DELETED, 'Timeseries Deleted'] + [MessageType.TIMESERIES_DELETED, 'Timeseries Deleted'], + [MessageType.RPC_QUEUED, 'RPC Queued'], + [MessageType.RPC_SENT, 'RPC Sent'], + [MessageType.RPC_DELIVERED, 'RPC Delivered'], + [MessageType.RPC_SUCCESSFUL, 'RPC Successful'], + [MessageType.RPC_TIMEOUT, 'RPC Timeout'], + [MessageType.RPC_FAILED, 'RPC Failed'] ] ); From 79c00d75c10808d650e32d5b4afde337fb90442b Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 14 Jun 2021 11:58:20 +0300 Subject: [PATCH 038/151] WIP: Refactoring of RPC --- .../secure/LwM2MBootstrapSecurityStore.java | 2 +- .../server/DefaultLwM2mTransportService.java | 1 + .../lwm2m/server/LwM2mOperationType.java | 78 +++++++ .../lwm2m/server/LwM2mServerListener.java | 1 + .../lwm2m/server/LwM2mSessionMsgListener.java | 19 +- .../server/LwM2mTransportCoapResource.java | 2 + .../lwm2m/server/LwM2mTransportUtil.java | 71 +------ .../lwm2m/server/client/LwM2mClient.java | 4 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 16 +- .../AbstractTbLwM2MRequestCallback.java | 2 +- .../DefaultLwM2mDownlinkMsgHandler.java | 2 +- .../TbLwM2MCancelAllObserveCallback.java | 4 +- .../downlink/TbLwM2MCancelAllRequest.java | 6 +- .../TbLwM2MCancelObserveCallback.java | 4 +- .../downlink/TbLwM2MCancelObserveRequest.java | 6 +- .../downlink/TbLwM2MDeleteCallback.java | 2 +- .../server/downlink/TbLwM2MDeleteRequest.java | 6 +- .../downlink/TbLwM2MDiscoverAllRequest.java | 6 +- .../downlink/TbLwM2MDiscoverCallback.java | 2 +- .../downlink/TbLwM2MDiscoverRequest.java | 6 +- .../downlink/TbLwM2MDownlinkRequest.java | 4 +- .../downlink/TbLwM2MExecuteCallback.java | 2 +- .../downlink/TbLwM2MExecuteRequest.java | 6 +- .../downlink/TbLwM2MObserveAllRequest.java | 6 +- .../downlink/TbLwM2MObserveCallback.java | 2 +- .../downlink/TbLwM2MObserveRequest.java | 6 +- .../server/downlink/TbLwM2MReadCallback.java | 2 +- .../server/downlink/TbLwM2MReadRequest.java | 6 +- .../TbLwM2MWriteAttributesCallback.java | 2 +- .../TbLwM2MWriteAttributesRequest.java | 6 +- .../downlink/TbLwM2MWriteReplaceCallback.java | 2 +- .../downlink/TbLwM2MWriteReplaceRequest.java | 6 +- .../downlink/TbLwM2MWriteUpdateRequest.java | 6 +- .../rpc/DefaultLwM2MRpcRequestHandler.java | 195 ++++++++++++++++++ .../lwm2m/server/rpc/IdOrKeyRequest.java | 26 +++ .../server/rpc/LwM2MRpcRequestHandler.java | 29 +++ .../LwM2mClientRpcRequest.java | 53 ++--- .../DefaultLwM2MUplinkMsgHandler.java | 135 +++--------- .../{ => uplink}/LwM2mUplinkMsgHandler.java | 16 +- 39 files changed, 469 insertions(+), 281 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcRequestHandler.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{client => rpc}/LwM2mClientRpcRequest.java (88%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => uplink}/DefaultLwM2MUplinkMsgHandler.java (91%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => uplink}/LwM2mUplinkMsgHandler.java (83%) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index e9b0edccf3..c23cc103df 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -161,7 +161,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, sessionInfo)); if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index c6711c31d3..97ab82f802 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java new file mode 100644 index 0000000000..694d7b96c7 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java @@ -0,0 +1,78 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server; + +/** + * Define the behavior of a write request. + */ +public enum LwM2mOperationType { + /** + * GET + */ + READ(0, "Read"), + DISCOVER(1, "Discover"), + DISCOVER_ALL(2, "DiscoverAll"), + OBSERVE_READ_ALL(3, "ObserveReadAll"), + /** + * POST + */ + OBSERVE(4, "Observe"), + OBSERVE_CANCEL(5, "ObserveCancel"), + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"), + EXECUTE(7, "Execute"), + /** + * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see + * section 5.3.3 of the LW M2M spec). + * if all resources are to be replaced + */ + WRITE_REPLACE(8, "WriteReplace"), + /* + PUT + */ + /** + * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section + * 5.3.3 of the LW M2M spec). + * if this is a partial update request + */ + WRITE_UPDATE(9, "WriteUpdate"), + WRITE_ATTRIBUTES(10, "WriteAttributes"), + DELETE(11, "Delete"), + + // only for RPC + FW_UPDATE(12, "FirmwareUpdate"); +// FW_READ_INFO(12, "FirmwareReadInfo"), + +// SW_READ_INFO(15, "SoftwareReadInfo"), +// SW_UPDATE(16, "SoftwareUpdate"), +// SW_UNINSTALL(18, "SoftwareUninstall"); + + public int code; + public String type; + + LwM2mOperationType(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2mOperationType fromType(String type) { + for (LwM2mOperationType to : LwM2mOperationType.values()) { + if (to.type.equals(type)) { + return to; + } + } + return null; + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 594148022c..97649603a6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -23,6 +23,7 @@ import org.eclipse.leshan.server.queue.PresenceListener; import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.RegistrationListener; import org.eclipse.leshan.server.registration.RegistrationUpdate; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Collection; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index 39a2194966..7448f9c71d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.thingsboard.server.common.data.Device; @@ -30,19 +31,19 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Optional; import java.util.UUID; @Slf4j +@RequiredArgsConstructor public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener { - private DefaultLwM2MUplinkMsgHandler handler; - private TransportProtos.SessionInfoProto sessionInfo; - - public LwM2mSessionMsgListener(DefaultLwM2MUplinkMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) { - this.handler = handler; - this.sessionInfo = sessionInfo; - } + private final LwM2mUplinkMsgHandler handler; + private final LwM2MRpcRequestHandler rpcHandler; + private final TransportProtos.SessionInfoProto sessionInfo; @Override public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { @@ -76,12 +77,12 @@ public class LwM2mSessionMsgListener implements GenericFutureListener implements DownlinkRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 189d359762..141fa61dac 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -53,7 +53,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index 87e219ef96..6d58853164 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL_ALL; public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java index 02ff01f973..f4e02e6222 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllRequest.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MCancelAllRequest implements TbLwM2MDownlinkRequest { @@ -30,8 +30,8 @@ public class TbLwM2MCancelAllRequest implements TbLwM2MDownlinkRequest } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; + public LwM2mOperationType getType() { + return LwM2mOperationType.OBSERVE_CANCEL_ALL; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index 8d9c13a428..d0fc5fdd6a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java index bf1ba36216..cb4d056578 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveRequest.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MCancelObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -26,8 +26,8 @@ public class TbLwM2MCancelObserveRequest extends AbstractTbLwM2MTargetedDownlink } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; + public LwM2mOperationType getType() { + return LwM2mOperationType.OBSERVE_CANCEL; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java index f6b0bfac81..8bd2e7505f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.DeleteResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MDeleteCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java index 62532c1726..b9649c0708 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteRequest.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import org.eclipse.leshan.core.response.ReadResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MDeleteRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -27,8 +27,8 @@ public class TbLwM2MDeleteRequest extends AbstractTbLwM2MTargetedDownlinkRequest } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.DELETE; + public LwM2mOperationType getType() { + return LwM2mOperationType.DELETE; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java index 9400e3bfba..291bdc907c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverAllRequest.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MDiscoverAllRequest implements TbLwM2MDownlinkRequest { @@ -30,8 +30,8 @@ public class TbLwM2MDiscoverAllRequest implements TbLwM2MDownlinkRequest } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; + public LwM2mOperationType getType() { + return LwM2mOperationType.DISCOVER_ALL; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java index dc3269b5a8..e21949f7c4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.DiscoverResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MDiscoverCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java index 6b8319bc25..85aa75de3c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverRequest.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import org.eclipse.leshan.core.response.DiscoverResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MDiscoverRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -27,8 +27,8 @@ public class TbLwM2MDiscoverRequest extends AbstractTbLwM2MTargetedDownlinkReque } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; + public LwM2mOperationType getType() { + return LwM2mOperationType.DISCOVER; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java index a069bb4ddc..3ea174c72d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDownlinkRequest.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public interface TbLwM2MDownlinkRequest { - LwM2mTransportUtil.LwM2mTypeOper getType(); + LwM2mOperationType getType(); long getTimeout(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java index f43a648cc3..ff7c40b3b9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.ExecuteResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java index 32d1be665c..76cf8cd0eb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteRequest.java @@ -18,7 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.response.ReadResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MExecuteRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -32,8 +32,8 @@ public class TbLwM2MExecuteRequest extends AbstractTbLwM2MTargetedDownlinkReques } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; + public LwM2mOperationType getType() { + return LwM2mOperationType.EXECUTE; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java index b12b4469d6..2aba4f0a58 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveAllRequest.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; import java.util.Set; @@ -32,8 +32,8 @@ public class TbLwM2MObserveAllRequest implements TbLwM2MDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java index 60fb9468f3..f3348aa2c9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveRequest.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.ObserveResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest implements HasContentFormat { @@ -33,8 +33,8 @@ public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkReques } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; + public LwM2mOperationType getType() { + return LwM2mOperationType.OBSERVE; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java index f142d53c09..9ab30a9410 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.ReadResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MReadCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java index 5d88a20ae4..a07e738465 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadRequest.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.ReadResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MReadRequest extends AbstractTbLwM2MTargetedDownlinkRequest implements HasContentFormat { @@ -33,8 +33,8 @@ public class TbLwM2MReadRequest extends AbstractTbLwM2MTargetedDownlinkRequest { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java index baa7401742..6ef198a28f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesRequest.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MWriteAttributesRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -33,8 +33,8 @@ public class TbLwM2MWriteAttributesRequest extends AbstractTbLwM2MTargetedDownli } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; + public LwM2mOperationType getType() { + return LwM2mOperationType.WRITE_ATTRIBUTES; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java index ecfa9241de..c8ef5622f2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.response.WriteResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MWriteReplaceCallback extends AbstractTbLwM2MRequestCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java index 488942f7de..007aa1d3f8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java @@ -18,7 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.response.WriteResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -32,8 +32,8 @@ public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkR } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; + public LwM2mOperationType getType() { + return LwM2mOperationType.WRITE_REPLACE; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java index 071d9fc889..a9465e082d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteUpdateRequest.java @@ -19,7 +19,7 @@ import lombok.Builder; import lombok.Getter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.WriteResponse; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MWriteUpdateRequest extends AbstractTbLwM2MTargetedDownlinkRequest { @@ -36,8 +36,8 @@ public class TbLwM2MWriteUpdateRequest extends AbstractTbLwM2MTargetedDownlinkRe } @Override - public LwM2mTransportUtil.LwM2mTypeOper getType() { - return LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; + public LwM2mOperationType getType() { + return LwM2mOperationType.WRITE_UPDATE; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java new file mode 100644 index 0000000000..18c4feba67 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -0,0 +1,195 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.server.registration.Registration; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; + +@Slf4j +@Service +@TbLwM2mTransportComponent +@RequiredArgsConstructor +public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { + + private final TransportService transportService; + private final LwM2mClientContext clientContext; + private final LwM2MTransportServerConfig config; + private final LwM2mUplinkMsgHandler uplinkHandler; + private final LwM2mDownlinkMsgHandler downlinkHandler; + private final Map rpcSubscriptions = new ConcurrentHashMap<>(); + + + @Override + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequst, TransportProtos.SessionInfoProto sessionInfo) { + this.cleanupOldSessions(); + UUID requestUUID = new UUID(rpcRequst.getRequestIdMSB(), rpcRequst.getRequestIdLSB()); + log.warn("Received params: {}", rpcRequst.getParams()); + // TODO: We use this map to protect from browser issue that the same command is sent twice. This is probably not the best place and should be moved to DeviceActor + if (!this.rpcSubscriptions.containsKey(requestUUID)) { + LwM2mOperationType operationType = LwM2mOperationType.fromType(rpcRequst.getMethodName()); + if (operationType == null) { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.METHOD_NOT_ALLOWED.getName(), "Unsupported operation type: " + rpcRequst.getMethodName()); + } + LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); + if (client.getRegistration() == null) { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.INTERNAL_SERVER_ERROR.getName(), "Registration is empty"); + } + switch (operationType) { + case READ: + sendReadRequest(client, rpcRequst); + break; + } + + +// log.warn("4) rpcRequst: [{}], sessionUUID: [{}]", rpcRequst, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); +// String bodyParams = StringUtils.trimToNull(rpcRequst.getParams()) != null ? rpcRequst.getParams() : "null"; +// LwM2mOperationType lwM2mTypeOper = setValidTypeOper(rpcRequst.getMethodName()); +// this.rpcSubscriptions.put(requestUUID, rpcRequst.getExpirationTime()); +// LwM2mClientRpcRequest lwm2mClientRpcRequest = null; +// try { +// LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); +// Registration registration = client.getRegistration(); +// if (registration != null) { +// lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, rpcRequst.getRequestId(), sessionInfo, registration, uplinkHandler); +// if (lwm2mClientRpcRequest.getErrorMsg() != null) { +// lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); +// this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); +// } else { +// //TODO: use different methods and RPC callback wrapper. +//// defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), +//// null, +//// lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), +//// this.config.getTimeout(), lwm2mClientRpcRequest); +// } +// } else { +// this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); +// } +// } catch (Exception e) { +// this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo); +// } + } + } + + private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { + String id = getIdFromParameters(rpcRequst); + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(id).timeout(this.config.getTimeout()).build(); + downlinkHandler.sendReadRequest(client, request, new TbLwM2MReadCallback(uplinkHandler, client, id)); + } + + private String getIdFromParameters(TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { + IdOrKeyRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), IdOrKeyRequest.class); + String id; + if (StringUtils.isNotEmpty(requestParams.getKey())) { + id = requestParams.getKey(); + } else if (StringUtils.isNotEmpty(requestParams.getId())) { + id = requestParams.getId(); + } else { + throw new IllegalArgumentException("Can't find 'key' or 'id' in the requestParams parameters!"); + } + return id; + } + + private String getTargetId(LwM2mClient client, String params) { + + } + + private void sendErrorRpcResponse(TransportProtos.SessionInfoProto sessionInfo, int requestId, String result, String error) { + String payload = JacksonUtil.toString(JacksonUtil.newObjectNode().put("result", result).put("error", error)); + TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build(); + transportService.process(sessionInfo, msg, null); + } + + private void sendErrorRpcResponse(LwM2mClientRpcRequest lwm2mClientRpcRequest, String msgError, TransportProtos.SessionInfoProto sessionInfo) { + if (lwm2mClientRpcRequest == null) { + lwm2mClientRpcRequest = new LwM2mClientRpcRequest(); + } + lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); + if (lwm2mClientRpcRequest.getErrorMsg() == null) { + lwm2mClientRpcRequest.setErrorMsg(msgError); + } + this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); + } + + private void cleanupOldSessions() { + log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); + if (rpcSubscriptions.size() > 0) { + long currentTime = System.currentTimeMillis(); + Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> currentTime > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); + log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); + log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); + rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); + } + log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); + } + + public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { + rpcRequest.setResponseCode(requestCode); + if (LOG_LW2M_ERROR.equals(typeMsg)) { + rpcRequest.setInfoMsg(null); + rpcRequest.setValueMsg(null); + if (rpcRequest.getErrorMsg() == null) { + msg = msg.isEmpty() ? null : msg; + rpcRequest.setErrorMsg(msg); + } + } else if (LOG_LW2M_INFO.equals(typeMsg)) { + if (rpcRequest.getInfoMsg() == null) { + rpcRequest.setInfoMsg(msg); + } + } else if (LOG_LW2M_VALUE.equals(typeMsg)) { + if (rpcRequest.getValueMsg() == null) { + rpcRequest.setValueMsg(msg); + } + } + this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); + } + + @Override + public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, TransportProtos.SessionInfoProto sessionInfo) { + log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + transportService.process(sessionInfo, toDeviceResponse, null); + } + + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { + log.info("[{}] toServerRpcResponse", toServerResponse); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java new file mode 100644 index 0000000000..1055b01232 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; + +@Data +public class IdOrKeyRequest { + + private String key; + private String id; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcRequestHandler.java new file mode 100644 index 0000000000..6d1b0c01bc --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcRequestHandler.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.thingsboard.server.gen.transport.TransportProtos; + +public interface LwM2MRpcRequestHandler { + + void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, TransportProtos.SessionInfoProto sessionInfo); + + void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponse, TransportProtos.SessionInfoProto sessionInfo); + + void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); + + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java similarity index 88% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java index e11d50f29f..07e47555fa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server.client; +package org.thingsboard.server.transport.lwm2m.server.rpc; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -24,7 +24,9 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Map; import java.util.Objects; @@ -36,15 +38,17 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; + +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.DISCOVER_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.FW_UPDATE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_READ_ALL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_ATTRIBUTES; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY; @@ -64,7 +68,7 @@ public class LwM2mClientRpcRequest { private String bodyParams; private int requestId; - private LwM2mTypeOper typeOper; + private LwM2mOperationType typeOper; private String key; private String targetIdVer; private Object value; @@ -78,8 +82,8 @@ public class LwM2mClientRpcRequest { public LwM2mClientRpcRequest() { } - public LwM2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, - TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MUplinkMsgHandler handler) { + public LwM2mClientRpcRequest(LwM2mOperationType lwM2mTypeOper, String bodyParams, int requestId, + TransportProtos.SessionInfoProto sessionInfo, Registration registration, LwM2mUplinkMsgHandler handler) { this.registration = registration; this.sessionInfo = sessionInfo; this.requestId = requestId; @@ -88,7 +92,7 @@ public class LwM2mClientRpcRequest { } else { this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid."; } - if (this.errorMsg == null && !bodyParams.equals("null")) { + if (this.errorMsg == null && !bodyParams.equals("null")) { this.bodyParams = bodyParams; this.init(handler); } @@ -110,7 +114,7 @@ public class LwM2mClientRpcRequest { .build(); } - private void init(DefaultLwM2MUplinkMsgHandler handler) { + private void init(LwM2mUplinkMsgHandler handler) { try { // #1 if (this.bodyParams.contains(KEY_NAME_KEY)) { @@ -179,7 +183,7 @@ public class LwM2mClientRpcRequest { } } - private void setValidParamsKey(DefaultLwM2MUplinkMsgHandler handler) { + private void setValidParamsKey(LwM2mUplinkMsgHandler handler) { String paramsStr = this.getValueKeyFromBody(PARAMS_KEY); if (paramsStr != null) { String params2Json = @@ -245,7 +249,7 @@ public class LwM2mClientRpcRequest { } private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, - DefaultLwM2MUplinkMsgHandler serviceImpl) { + LwM2mUplinkMsgHandler serviceImpl) { Map paramsIdVer = new ConcurrentHashMap<>(); LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.targetIdVer))); if (targetId.isObjectInstance()) { @@ -257,7 +261,7 @@ public class LwM2mClientRpcRequest { String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k); if (targetIdVer != null) { LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(targetIdVer))); - paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); + paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); } /** WRITE_UPDATE*/ else { @@ -272,10 +276,11 @@ public class LwM2mClientRpcRequest { return (ConcurrentHashMap) paramsIdVer; } - private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MUplinkMsgHandler handler) { - LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo); - return lwM2mClient != null ? - lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : - null; + private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, LwM2mUplinkMsgHandler handler) { +// LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo); +// return lwM2mClient != null ? +// lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : +// null; + return null; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java similarity index 91% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 55129e59c9..b64345bf54 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.uplink; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -21,7 +21,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mObject; @@ -55,30 +54,36 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; +import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; +import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -98,7 +103,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; @@ -110,16 +114,14 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @@ -141,12 +143,14 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; public final LwM2mClientContext clientContext; + private final LwM2MRpcRequestHandler rpcHandler; public final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; - private final Map rpcSubscriptions; + public final Map firmwareUpdateState; public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, + @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { @@ -154,11 +158,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.config = config; this.helper = helper; this.clientContext = clientContext; + this.rpcHandler = rpcHandler; this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; this.otaPackageDataCache = otaPackageDataCache; this.context = context; this.adaptor = adaptor; - this.rpcSubscriptions = new ConcurrentHashMap<>(); this.firmwareUpdateState = new ConcurrentHashMap<>(); this.sessionStore = sessionStore; } @@ -195,7 +199,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.clientContext.register(lwM2MClient, registration); this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId()); SessionInfoProto sessionInfo = lwM2MClient.getSession(); - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) @@ -350,7 +354,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { READ, pathIdVer, value); this.sendLogsToThingsboard(lwM2MClient, msg); rpcRequest.setValueMsg(String.format("%s", value)); - this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); + //TODO: refactor +// this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); } /** @@ -452,98 +457,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider())); } - /** - * #1 del from rpcSubscriptions by timeout - * #2 if not present in rpcSubscriptions by requestId: create new LwM2mClientRpcRequest, after success - add requestId, timeout - */ - @Override - public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { - // #1 - this.checkRpcRequestTimeout(); - log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); - String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; - LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); - UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); - if (!this.rpcSubscriptions.containsKey(requestUUID)) { - this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); - LwM2mClientRpcRequest lwm2mClientRpcRequest = null; - try { - LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); - Registration registration = client.getRegistration(); - if (registration != null) { - lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); - if (lwm2mClientRpcRequest.getErrorMsg() != null) { - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); - } else { - //TODO: use different methods and RPC callback wrapper. -// defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), -// null, -// lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), -// this.config.getTimeout(), lwm2mClientRpcRequest); - } - } else { - this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); - } - } catch (Exception e) { - this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo); - } - } - } - - private void sendErrorRpcResponse(LwM2mClientRpcRequest lwm2mClientRpcRequest, String msgError, SessionInfoProto sessionInfo) { - if (lwm2mClientRpcRequest == null) { - lwm2mClientRpcRequest = new LwM2mClientRpcRequest(); - } - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); - if (lwm2mClientRpcRequest.getErrorMsg() == null) { - lwm2mClientRpcRequest.setErrorMsg(msgError); - } - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); - } - - private void checkRpcRequestTimeout() { - log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); - if (rpcSubscriptions.size() > 0) { - Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); - log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); - log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); - rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); - } - log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); - } - - public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { - rpcRequest.setResponseCode(requestCode); - if (LOG_LW2M_ERROR.equals(typeMsg)) { - rpcRequest.setInfoMsg(null); - rpcRequest.setValueMsg(null); - if (rpcRequest.getErrorMsg() == null) { - msg = msg.isEmpty() ? null : msg; - rpcRequest.setErrorMsg(msg); - } - } else if (LOG_LW2M_INFO.equals(typeMsg)) { - if (rpcRequest.getInfoMsg() == null) { - rpcRequest.setInfoMsg(msg); - } - } else if (LOG_LW2M_VALUE.equals(typeMsg)) { - if (rpcRequest.getValueMsg() == null) { - rpcRequest.setValueMsg(msg); - } - } - this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); - } - - @Override - public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { - log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); - transportService.process(sessionInfo, toDeviceResponse, null); - } - - public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { - log.info("[{}] toServerRpcResponse", toServerResponse); - } - /** * Deregister session in transport * @@ -1065,6 +978,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) * config attr/telemetry... in profile */ + @Override public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) { log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); } @@ -1076,6 +990,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param name - * @return - */ + @Override public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { var profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo); @@ -1093,6 +1008,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param attributesResponse - * @param sessionInfo - */ + @Override public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { try { List tsKvProtos = attributesResponse.getSharedAttributeListList(); @@ -1163,7 +1079,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { */ private void reportActivityAndRegister(SessionInfoProto sessionInfo) { if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); this.reportActivitySubscription(sessionInfo); } } @@ -1236,7 +1152,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace(msgError); if (rpcRequest != null) { - sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); + //TODO: refactor +// sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java similarity index 83% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 132bd8689d..91985363eb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.uplink; import org.eclipse.leshan.core.observation.Observation; import org.eclipse.leshan.core.response.ReadResponse; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import java.util.Collection; import java.util.Optional; @@ -50,12 +50,6 @@ public interface LwM2mUplinkMsgHandler { void onResourceDelete(Optional resourceDeleteMsgOpt); - void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, TransportProtos.SessionInfoProto sessionInfo); - - void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponse, TransportProtos.SessionInfoProto sessionInfo); - - void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); - void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void onAwakeDev(Registration registration); @@ -64,5 +58,11 @@ public interface LwM2mUplinkMsgHandler { void sendLogsToThingsboard(String registrationId, String msg); + void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); + + String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name); + + void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo); + LwM2MTransportServerConfig getConfig(); } From e58d5b2d8a7e3760525ce5c9a0f497b299a9625d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 14 Jun 2021 15:06:21 +0300 Subject: [PATCH 039/151] removed rpc status sent, added idx_rpc_tenant_id_device_id --- .../main/data/upgrade/3.2.2/schema_update.sql | 2 + .../device/DeviceActorMessageProcessor.java | 52 +++++++--------- .../server/controller/RpcController.java | 39 +++++++++++- .../action/RuleEngineEntityActionService.java | 2 +- .../server/service/rpc/TbRpcService.java | 11 ++-- .../server/dao/rpc/RpcService.java | 8 ++- .../server/common/data/DataConstants.java | 1 - .../server/common/data/rpc/RpcStatus.java | 2 +- .../server/dao/rpc/BaseRpcService.java | 60 +++++++++++++++---- .../thingsboard/server/dao/rpc/RpcDao.java | 4 +- .../server/dao/sql/rpc/JpaRpcDao.java | 8 ++- .../server/dao/sql/rpc/RpcRepository.java | 4 +- .../server/dao/tenant/TenantServiceImpl.java | 5 ++ .../resources/sql/schema-entities-idx.sql | 1 + .../engine/filter/TbMsgTypeSwitchNode.java | 4 +- .../src/app/shared/models/rule-node.models.ts | 6 +- 16 files changed, 147 insertions(+), 62 deletions(-) diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 713793b341..d4404427f2 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -209,3 +209,5 @@ CREATE TABLE IF NOT EXISTS rpc ( status varchar(255) NOT NULL ); +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id); + diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 8657313633..a1458fd5ac 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -174,20 +174,15 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { long timeout = request.getExpirationTime() - System.currentTimeMillis(); boolean persisted = request.isPersisted(); + if (timeout <= 0) { log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); - if (persisted) { - Rpc rpc = new Rpc(new RpcId(request.getId())); - rpc.setCreatedTime(System.currentTimeMillis()); - rpc.setTenantId(tenantId); - rpc.setDeviceId(deviceId); - rpc.setExpirationTime(request.getExpirationTime()); - rpc.setRequest(JacksonUtil.valueToTree(request)); - rpc.setStatus(RpcStatus.TIMEOUT); - systemContext.getTbRpcService().save(tenantId, rpc); + createRpc(request, RpcStatus.TIMEOUT); } return; + } else if (persisted) { + createRpc(request, RpcStatus.QUEUED); } boolean sent; @@ -204,24 +199,14 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { syncSessionSet.add(key); } }); - log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions); syncSessionSet.forEach(rpcSubscriptions::remove); } - if (persisted) { - Rpc rpc = new Rpc(new RpcId(request.getId())); - rpc.setCreatedTime(System.currentTimeMillis()); - rpc.setTenantId(tenantId); - rpc.setDeviceId(deviceId); - rpc.setExpirationTime(request.getExpirationTime()); - rpc.setRequest(JacksonUtil.valueToTree(request)); - rpc.setStatus(sent ? RpcStatus.SENT : RpcStatus.QUEUED); - systemContext.getTbRpcService().save(tenantId, rpc); - if (!(sent || request.isOneway())) { - ObjectNode response = JacksonUtil.newObjectNode(); - response.put("rpcId", request.getId().toString()); - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null)); - } + if (persisted && !(sent || request.isOneway())) { + ObjectNode response = JacksonUtil.newObjectNode(); + response.put("rpcId", request.getId().toString()); + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null)); } if (request.isOneway() && sent) { @@ -237,6 +222,18 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } } + private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) { + Rpc rpc = new Rpc(new RpcId(request.getId())); + rpc.setCreatedTime(System.currentTimeMillis()); + rpc.setTenantId(tenantId); + rpc.setDeviceId(deviceId); + rpc.setExpirationTime(request.getExpirationTime()); + rpc.setRequest(JacksonUtil.valueToTree(request)); + rpc.setStatus(status); + systemContext.getTbRpcService().save(tenantId, rpc); + return systemContext.getTbRpcService().save(tenantId, rpc); + } + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) { ToDeviceRpcRequestBody body = request.getBody(); return ToDeviceRpcRequestMsg.newBuilder() @@ -318,9 +315,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { .setPersisted(request.isPersisted()) .build(); - if (request.isPersisted()) { - systemContext.getTbRpcService().save(tenantId, new RpcId(request.getId()), RpcStatus.SENT, null); - } sendToTransport(rpcRequest, sessionId, nodeId); }; } @@ -821,10 +815,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { void init(TbActorCtx ctx) { schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout()); - PageLink pageLink = new PageLink(10); + PageLink pageLink = new PageLink(1024); PageData pageData; do { - pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(deviceId, RpcStatus.QUEUED, pageLink); + pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink); pageData.getData().forEach(rpc -> { ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcController.java b/application/src/main/java/org/thingsboard/server/controller/RpcController.java index 8a75aad485..eae26542fa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcController.java @@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; @@ -41,7 +42,10 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -95,7 +99,7 @@ public class RpcController extends BaseController { return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/persisted/{rpcId}", method = RequestMethod.GET) @ResponseBody public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException { @@ -108,6 +112,39 @@ public class RpcController extends BaseController { } } + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/persisted/{deviceId}", method = RequestMethod.GET) + @ResponseBody + public PageData getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId, + @RequestParam int pageSize, + @RequestParam int page, + @RequestParam RpcStatus rpcStatus, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + checkParameter("DeviceId", strDeviceId); + try { + TenantId tenantId = getCurrentUser().getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId)); + return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/persisted/{rpcId}", method = RequestMethod.DELETE) + @ResponseBody + public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException { + checkParameter("RpcId", strRpc); + try { + rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc))); + } catch (Exception e) { + throw handleException(e); + } + } + private DeferredResult handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { try { JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); diff --git a/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java index 754d32b704..f1320d1a7a 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java @@ -54,7 +54,7 @@ public class RuleEngineEntityActionService { private static final ObjectMapper json = new ObjectMapper(); - public void pushEntityActionToRuleEngine(EntityId entityId, Object entity, TenantId tenantId, CustomerId customerId, + public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, ActionType actionType, User user, Object... additionalInfo) { String msgType = null; switch (actionType) { diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index d410f12b1f..e0343485db 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -41,9 +41,10 @@ public class TbRpcService { private final RpcService rpcService; private final TbClusterService tbClusterService; - public void save(TenantId tenantId, Rpc rpc) { - Rpc saved = rpcService.save(tenantId, rpc); + public Rpc save(TenantId tenantId, Rpc rpc) { + Rpc saved = rpcService.save(rpc); pushRpcMsgToRuleEngine(tenantId, saved); + return saved; } public void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode response) { @@ -53,7 +54,7 @@ public class TbRpcService { if (response != null) { foundRpc.setResponse(response); } - Rpc saved = rpcService.save(tenantId, foundRpc); + Rpc saved = rpcService.save(foundRpc); pushRpcMsgToRuleEngine(tenantId, saved); } else { log.warn("[{}] Failed to update RPC status because RPC was already deleted", rpcId); @@ -69,8 +70,8 @@ public class TbRpcService { return rpcService.findById(tenantId, rpcId); } - public PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { - return rpcService.findAllByDeviceIdAndStatus(deviceId, rpcStatus, pageLink); + public PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + return rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java index 22446d143e..4bdb1a169d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java @@ -25,13 +25,15 @@ import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.RpcStatus; public interface RpcService { - Rpc save(TenantId tenantId, Rpc rpc); + Rpc save(Rpc rpc); - void remove(TenantId tenantId, RpcId id); + void deleteRpc(TenantId tenantId, RpcId id); + + void deleteAllRpcByTenantId(TenantId tenantId); Rpc findById(TenantId tenantId, RpcId id); ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId id); - PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); + PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index de97bf324c..efc81846d5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -77,7 +77,6 @@ public class DataConstants { public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; public static final String RPC_QUEUED = "RPC_QUEUED"; - public static final String RPC_SENT = "RPC_SENT"; public static final String RPC_DELIVERED = "RPC_DELIVERED"; public static final String RPC_SUCCESSFUL = "RPC_SUCCESSFUL"; public static final String RPC_TIMEOUT = "RPC_TIMEOUT"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java index fea9e30a75..c80d0c5993 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.data.rpc; public enum RpcStatus { - QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, FAILED + QUEUED, DELIVERED, SUCCESSFUL, TIMEOUT, FAILED } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java index 577263a3bd..02b4bbe433 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java @@ -26,35 +26,75 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.service.PaginatedRemover; + +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; @Service @Slf4j @RequiredArgsConstructor public class BaseRpcService implements RpcService { + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_RPC_ID = "Incorrect rpcId "; + private final RpcDao rpcDao; @Override - public Rpc save(TenantId tenantId, Rpc rpc) { - return rpcDao.save(tenantId, rpc); + public Rpc save(Rpc rpc) { + log.trace("Executing save, [{}]", rpc); + return rpcDao.save(rpc.getTenantId(), rpc); } @Override - public void remove(TenantId tenantId, RpcId id) { - rpcDao.removeById(tenantId, id.getId()); + public void deleteRpc(TenantId tenantId, RpcId rpcId) { + log.trace("Executing deleteRpc, tenantId [{}], rpcId [{}]", tenantId, rpcId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(rpcId, INCORRECT_RPC_ID + rpcId); + rpcDao.removeById(tenantId, rpcId.getId()); } @Override - public Rpc findById(TenantId tenantId, RpcId id) { - return rpcDao.findById(tenantId, id.getId()); + public void deleteAllRpcByTenantId(TenantId tenantId) { + log.trace("Executing deleteAllRpcByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + tenantRpcRemover.removeEntities(tenantId, tenantId); } @Override - public ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId id) { - return rpcDao.findByIdAsync(tenantId, id.getId()); + public Rpc findById(TenantId tenantId, RpcId rpcId) { + log.trace("Executing findById, tenantId [{}], rpcId [{}]", tenantId, rpcId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(rpcId, INCORRECT_RPC_ID + rpcId); + return rpcDao.findById(tenantId, rpcId.getId()); } @Override - public PageData findAllByDeviceIdAndStatus(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { - return rpcDao.findAllByDeviceId(deviceId, rpcStatus, pageLink); + public ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId rpcId) { + log.trace("Executing findRpcByIdAsync, tenantId [{}], rpcId: [{}]", tenantId, rpcId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(rpcId, INCORRECT_RPC_ID + rpcId); + return rpcDao.findByIdAsync(tenantId, rpcId.getId()); } + + @Override + public PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return rpcDao.findAllByDeviceId(tenantId, deviceId, rpcStatus, pageLink); + } + + private PaginatedRemover tenantRpcRemover = + new PaginatedRemover<>() { + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return rpcDao.findAllRpcByTenantId(id, pageLink); + } + + @Override + protected void removeEntity(TenantId tenantId, Rpc entity) { + deleteRpc(tenantId, entity.getId()); + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index a3aa32720a..63af784dbb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.dao.Dao; public interface RpcDao extends Dao { - PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); + PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink); + + PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink); Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 22fc3396f7..221ef17361 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -49,10 +49,14 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao return rpcRepository; } + @Override + public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { + return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceIdAndStatus(tenantId.getId(), deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink))); + } @Override - public PageData findAllByDeviceId(DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { - return DaoUtil.toPageData(rpcRepository.findAllByDeviceIdAndStatus(deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink))); + public PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(rpcRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index 565d87e627..76b67b3823 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -26,7 +26,9 @@ import org.thingsboard.server.dao.model.sql.RpcEntity; import java.util.UUID; public interface RpcRepository extends CrudRepository { - Page findAllByDeviceIdAndStatus(UUID deviceId, RpcStatus status, Pageable pageable); + Page findAllByTenantIdAndDeviceIdAndStatus(UUID tenantId, UUID deviceId, RpcStatus status, Pageable pageable); + + Page findAllByTenantId(UUID tenantId, Pageable pageable); @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted", nativeQuery = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index c5050467dd..eab0a070f5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -37,6 +37,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -96,6 +97,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private OtaPackageService otaPackageService; + @Autowired + private RpcService rpcService; + @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); @@ -151,6 +155,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); resourceService.deleteResourcesByTenantId(tenantId); otaPackageService.deleteOtaPackagesByTenantId(tenantId); + rpcService.deleteAllRpcByTenantId(tenantId); tenantDao.removeById(tenantId, tenantId.getId()); deleteEntityRelations(tenantId, tenantId); } diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 28bbe4311e..d649e44418 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -46,3 +46,4 @@ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribu CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index 235bdcfa44..85c2a156b3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; type = ComponentType.FILTER, name = "message type switch", configClazz = EmptyNodeConfiguration.class, - relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "RPC Queued", "RPC Sent", "RPC Delivered", "RPC Successful", "RPC Timeout", "RPC Failed", + relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "RPC Queued", "RPC Delivered", "RPC Successful", "RPC Timeout", "RPC Failed", "Activity Event", "Inactivity Event", "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant", "Timeseries Updated", "Timeseries Deleted"}, @@ -97,8 +97,6 @@ public class TbMsgTypeSwitchNode implements TbNode { relationType = "Timeseries Deleted"; } else if (msg.getType().equals(DataConstants.RPC_QUEUED)) { relationType = "RPC Queued"; - } else if (msg.getType().equals(DataConstants.RPC_SENT)) { - relationType = "RPC Sent"; } else if (msg.getType().equals(DataConstants.RPC_DELIVERED)) { relationType = "RPC Delivered"; } else if (msg.getType().equals(DataConstants.RPC_SUCCESSFUL)) { diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index 49c3f18bf8..489c6eef10 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -354,9 +354,8 @@ export enum MessageType { TIMESERIES_UPDATED = 'TIMESERIES_UPDATED', TIMESERIES_DELETED = 'TIMESERIES_DELETED', RPC_QUEUED = 'RPC_QUEUED', - RPC_SENT = 'RPC_SENT', - RPC_DELIVERED = 'RPC_SENT', - RPC_SUCCESSFUL = 'RPC_DELIVERED', + RPC_DELIVERED = 'RPC_DELIVERED', + RPC_SUCCESSFUL = 'RPC_SUCCESSFUL', RPC_TIMEOUT = 'RPC_TIMEOUT', RPC_FAILED = 'RPC_FAILED' } @@ -381,7 +380,6 @@ export const messageTypeNames = new Map( [MessageType.TIMESERIES_UPDATED, 'Timeseries Updated'], [MessageType.TIMESERIES_DELETED, 'Timeseries Deleted'], [MessageType.RPC_QUEUED, 'RPC Queued'], - [MessageType.RPC_SENT, 'RPC Sent'], [MessageType.RPC_DELIVERED, 'RPC Delivered'], [MessageType.RPC_SUCCESSFUL, 'RPC Successful'], [MessageType.RPC_TIMEOUT, 'RPC Timeout'], From f862034f79172e4e32d23c17e0b03c0bf098831f Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 14 Jun 2021 18:40:14 +0300 Subject: [PATCH 040/151] Implementation of LwM2M RPC commands --- .../lwm2m/server/LwM2mOperationType.java | 53 ++--- .../lwm2m/server/client/LwM2mClient.java | 25 +-- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 6 +- .../DefaultLwM2mDownlinkMsgHandler.java | 83 ++++---- .../downlink/LwM2mDownlinkMsgHandler.java | 4 +- ...java => TbLwM2MWriteResponseCallback.java} | 4 +- .../rpc/DefaultLwM2MRpcRequestHandler.java | 200 ++++++++++++++---- .../server/rpc/LwM2MRpcResponseBody.java | 32 +++ .../server/rpc/RpcCancelObserveCallback.java | 41 ++++ .../lwm2m/server/rpc/RpcDiscoverCallback.java | 43 ++++ .../rpc/RpcDownlinkRequestCallbackProxy.java | 86 ++++++++ .../server/rpc/RpcEmptyResponseCallback.java | 38 ++++ .../lwm2m/server/rpc/RpcLinkSetCallback.java | 41 ++++ .../server/rpc/RpcLwM2MDownlinkCallback.java | 50 +++++ .../server/rpc/RpcReadResponseCallback.java | 50 +++++ .../server/rpc/RpcWriteAttributesRequest.java | 28 +++ .../server/rpc/RpcWriteReplaceRequest.java | 27 +++ .../server/rpc/RpcWriteUpdateRequest.java | 29 +++ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 29 +-- 19 files changed, 719 insertions(+), 150 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/{TbLwM2MWriteReplaceCallback.java => TbLwM2MWriteResponseCallback.java} (84%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java index 694d7b96c7..f9b3f93854 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java @@ -15,56 +15,57 @@ */ package org.thingsboard.server.transport.lwm2m.server; +import lombok.Getter; + /** * Define the behavior of a write request. */ public enum LwM2mOperationType { - /** - * GET - */ - READ(0, "Read"), - DISCOVER(1, "Discover"), - DISCOVER_ALL(2, "DiscoverAll"), - OBSERVE_READ_ALL(3, "ObserveReadAll"), - /** - * POST - */ - OBSERVE(4, "Observe"), - OBSERVE_CANCEL(5, "ObserveCancel"), - OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"), - EXECUTE(7, "Execute"), + + READ(0, "Read", true), + DISCOVER(1, "Discover", true), + DISCOVER_ALL(2, "DiscoverAll", false), + OBSERVE_READ_ALL(3, "ObserveReadAll", false), + + OBSERVE(4, "Observe", true), + OBSERVE_CANCEL(5, "ObserveCancel", true), + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll", false), + EXECUTE(7, "Execute", true), /** * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see * section 5.3.3 of the LW M2M spec). * if all resources are to be replaced */ - WRITE_REPLACE(8, "WriteReplace"), - /* - PUT - */ + WRITE_REPLACE(8, "WriteReplace", true), + /** * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section * 5.3.3 of the LW M2M spec). * if this is a partial update request */ - WRITE_UPDATE(9, "WriteUpdate"), - WRITE_ATTRIBUTES(10, "WriteAttributes"), - DELETE(11, "Delete"), + WRITE_UPDATE(9, "WriteUpdate", true), + WRITE_ATTRIBUTES(10, "WriteAttributes", true), + DELETE(11, "Delete", true), // only for RPC - FW_UPDATE(12, "FirmwareUpdate"); -// FW_READ_INFO(12, "FirmwareReadInfo"), + FW_UPDATE(12, "FirmwareUpdate", false); +// FW_READ_INFO(12, "FirmwareReadInfo"), // SW_READ_INFO(15, "SoftwareReadInfo"), // SW_UPDATE(16, "SoftwareUpdate"), // SW_UNINSTALL(18, "SoftwareUninstall"); - public int code; - public String type; + @Getter + private final int code; + @Getter + private final String type; + @Getter + private final boolean hasObjectId; - LwM2mOperationType(int code, String type) { + LwM2mOperationType(int code, String type, boolean hasObjectId) { this.code = code; this.type = type; + this.hasObjectId = hasObjectId; } public static LwM2mOperationType fromType(String type) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 1ce9358078..77454d8b83 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.registration.Registration; @@ -40,7 +41,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import java.util.Collection; import java.util.List; @@ -257,7 +257,7 @@ public class LwM2mClient implements Cloneable { .getObjectModel(pathIds.getObjectId()) : null; } - public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) { + public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverter converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={"); lwM2mObject.getInstances().forEach((instId, inst) -> { @@ -271,7 +271,7 @@ public class LwM2mClient implements Cloneable { return builder.toString(); } - public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) { + public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverter converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={"); objectInstance.getResources().forEach((resId, res) -> { @@ -285,19 +285,12 @@ public class LwM2mClient implements Cloneable { return builder.toString(); } - public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) { - if (!OPAQUE.equals(lwM2mResource.getType())) { - return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() : - ((LwM2mSingleResource) lwM2mResource).toString(); - } else { - return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(), - converter.convertValue(lwM2mResource.getValue(), - OPAQUE, STRING, new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))), lwM2mResource.getType().name()); - } + public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverter converter, String pathIdVer) { + return lwM2mResource.getValue().toString(); } public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, - LwM2mValueConverterImpl converter) { + LwM2mValueConverter converter) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) @@ -313,7 +306,7 @@ public class LwM2mClient implements Cloneable { } public Collection getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, - LwM2mValueConverterImpl converter) { + LwM2mValueConverter converter) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) @@ -395,7 +388,7 @@ public class LwM2mClient implements Cloneable { } } - public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { if (this.fwUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); this.fwUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); @@ -403,7 +396,7 @@ public class LwM2mClient implements Cloneable { return this.fwUpdate; } - public LwM2mFwSwUpdate getSwUpdate (LwM2mUplinkMsgHandler handler,LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { if (this.swUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); this.swUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index d4efda61ab..6896c3d8de 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -33,7 +33,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import java.util.ArrayList; @@ -201,14 +201,14 @@ public class LwM2mFwSwUpdate { byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { String apiFont = "coap://176.36.143.9:5685"; String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); //TODO: user this.rpcRequest??? TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 141fa61dac..837e2a17bb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -47,6 +47,7 @@ import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -98,30 +99,30 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { @Override public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); - sendRequest(client, downlink, request.getTimeout(), callback); - } + validateVersionedId(client, request); + ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); + sendRequest(client, downlink, request.getTimeout(), callback); } @Override public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); - Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); - if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { - ObserveRequest downlink; - ContentFormat contentFormat = getContentFormat(client, request); - if (resultIds.isResource()) { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); - } else if (resultIds.isObjectInstance()) { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); - } else { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); - } - log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); - sendRequest(client, downlink, request.getTimeout(), callback); + validateVersionedId(client, request); + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); + Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); + if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { + ObserveRequest downlink; + ContentFormat contentFormat = getContentFormat(client, request); + if (resultIds.isResource()) { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + } else if (resultIds.isObjectInstance()) { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); + } else { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); } + log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); + sendRequest(client, downlink, request.getTimeout(), callback); + } else { + throw new IllegalArgumentException("Observation is already registered!"); } } @@ -133,10 +134,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { - Link[] objectLinks = client.getRegistration().getSortedObjectLinks(); - Set paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); - callback.onSuccess(paths); + public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { + callback.onSuccess(Arrays.asList(client.getRegistration().getSortedObjectLinks())); } @Override @@ -172,27 +171,28 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { @Override public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); - } + validateVersionedId(client, request); + sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); } @Override public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId()) && request.getAttributes() != null) { - ObjectAttributes params = request.getAttributes(); - List attributes = new LinkedList<>(); + validateVersionedId(client, request); + if (request.getAttributes() == null) { + throw new IllegalArgumentException("Attributes to write are not specified!"); + } + ObjectAttributes params = request.getAttributes(); + List attributes = new LinkedList<>(); // Dimension and Object version are read only attributes. // addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); // addAttribute(attributes, OBJECT_VERSION, params.getVer(), StringUtils::isNotEmpty, Function.identity()); - addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); - addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); - addAttribute(attributes, GREATER_THAN, params.getGt()); - addAttribute(attributes, LESSER_THAN, params.getLt()); - addAttribute(attributes, STEP, params.getSt()); - AttributeSet attributeSet = new AttributeSet(attributes); - sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); - } + addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); + addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); + addAttribute(attributes, GREATER_THAN, params.getGt()); + addAttribute(attributes, LESSER_THAN, params.getLt()); + addAttribute(attributes, STEP, params.getSt()); + AttributeSet attributeSet = new AttributeSet(attributes); + sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); } @Override @@ -642,7 +642,14 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { // handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); // } // } - + private void validateVersionedId(LwM2mClient client, HasVersionedId request) { + if (!client.isValidObjectVersion(request.getVersionedId())) { + throw new IllegalArgumentException("Specified resource id is not configured in the device profile!"); + } + if (request.getObjectId() == null) { + throw new IllegalArgumentException("Specified object id is null!"); + } + } private static void addAttribute(List attributes, String attributeName, T value) { addAttribute(attributes, attributeName, value, null, null); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java index 473205eaa1..724dc78993 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ExecuteResponse; @@ -24,6 +25,7 @@ import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.List; import java.util.Set; public interface LwM2mDownlinkMsgHandler { @@ -44,7 +46,7 @@ public interface LwM2mDownlinkMsgHandler { void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback); - void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); + void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java similarity index 84% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index c8ef5622f2..2cee4887c9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -19,11 +19,11 @@ import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MWriteReplaceCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MWriteReplaceCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 4da5409712..0c371dac4e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -29,8 +29,27 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Map; @@ -57,62 +76,167 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private final LwM2mDownlinkMsgHandler downlinkHandler; private final Map rpcSubscriptions = new ConcurrentHashMap<>(); - @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequst, TransportProtos.SessionInfoProto sessionInfo) { this.cleanupOldSessions(); UUID requestUUID = new UUID(rpcRequst.getRequestIdMSB(), rpcRequst.getRequestIdLSB()); log.warn("Received params: {}", rpcRequst.getParams()); - // TODO: We use this map to protect from browser issue that the same command is sent twice. This is probably not the best place and should be moved to DeviceActor + // We use this map to protect from browser issue that the same command is sent twice. + // TODO: This is probably not the best place and should be moved to DeviceActor if (!this.rpcSubscriptions.containsKey(requestUUID)) { LwM2mOperationType operationType = LwM2mOperationType.fromType(rpcRequst.getMethodName()); if (operationType == null) { this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.METHOD_NOT_ALLOWED.getName(), "Unsupported operation type: " + rpcRequst.getMethodName()); + return; } LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); if (client.getRegistration() == null) { this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.INTERNAL_SERVER_ERROR.getName(), "Registration is empty"); + return; } - switch (operationType) { - case READ: - sendReadRequest(client, rpcRequst); - break; + try { + if (operationType.isHasObjectId()) { + String objectId = getIdFromParameters(client, rpcRequst); + switch (operationType) { + case READ: + sendReadRequest(client, rpcRequst, objectId); + break; + case OBSERVE: + sendObserveRequest(client, rpcRequst, objectId); + break; + case DISCOVER: + sendDiscoverRequest(client, rpcRequst, objectId); + break; + case EXECUTE: + sendExecuteRequest(client, rpcRequst, objectId); + break; + case WRITE_ATTRIBUTES: + sendWriteAttributesRequest(client, rpcRequst, objectId); + break; + case OBSERVE_CANCEL: + sendCancelObserveRequest(client, rpcRequst, objectId); + break; + case DELETE: + sendDeleteRequest(client, rpcRequst, objectId); + break; + case WRITE_UPDATE: + sendWriteUpdateRequest(client, rpcRequst, objectId); + break; + case WRITE_REPLACE: + sendWriteReplaceRequest(client, rpcRequst, objectId); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } else { + switch (operationType) { + case OBSERVE_CANCEL_ALL: + sendCancelAllObserveRequest(client, rpcRequst); + break; + case OBSERVE_READ_ALL: + sendObserveAllRequest(client, rpcRequst); + break; + case DISCOVER_ALL: + sendDiscoverAllRequest(client, rpcRequst); + break; + case FW_UPDATE: + //TODO: implement and add break statement + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } + } catch (IllegalArgumentException e) { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.BAD_REQUEST.getName(), e.getMessage()); } + } + } + private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MReadCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); + downlinkHandler.sendReadRequest(client, request, rpcCallback); + } -// log.warn("4) rpcRequst: [{}], sessionUUID: [{}]", rpcRequst, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); -// String bodyParams = StringUtils.trimToNull(rpcRequst.getParams()) != null ? rpcRequst.getParams() : "null"; -// LwM2mOperationType lwM2mTypeOper = setValidTypeOper(rpcRequst.getMethodName()); -// this.rpcSubscriptions.put(requestUUID, rpcRequst.getExpirationTime()); -// LwM2mClientRpcRequest lwm2mClientRpcRequest = null; -// try { -// LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); -// Registration registration = client.getRegistration(); -// if (registration != null) { -// lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, rpcRequst.getRequestId(), sessionInfo, registration, uplinkHandler); -// if (lwm2mClientRpcRequest.getErrorMsg() != null) { -// lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); -// this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); -// } else { -// //TODO: use different methods and RPC callback wrapper. -//// defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), -//// null, -//// lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), -//// this.config.getTimeout(), lwm2mClientRpcRequest); -// } -// } else { -// this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); -// } -// } catch (Exception e) { -// this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo); -// } - } + private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); + downlinkHandler.sendObserveRequest(client, request, rpcCallback); + } + + private void sendObserveAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MObserveAllRequest request = TbLwM2MObserveAllRequest.builder().timeout(this.config.getTimeout()).build(); + downlinkHandler.sendObserveAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + } + + private void sendDiscoverAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MDiscoverAllRequest request = TbLwM2MDiscoverAllRequest.builder().timeout(this.config.getTimeout()).build(); + downlinkHandler.sendDiscoverAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + } + + private void sendDiscoverRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MDiscoverCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcDiscoverCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendDiscoverRequest(client, request, rpcCallback); + } + + private void sendExecuteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MExecuteCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendExecuteRequest(client, downlink, rpcCallback); + } + + private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class); + TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId) + .attributes(requestBody.getAttributes()) + .timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MWriteAttributesCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteAttributesRequest(client, request, rpcCallback); + } + + private void sendWriteUpdateRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); + TbLwM2MWriteUpdateRequest.TbLwM2MWriteUpdateRequestBuilder builder = TbLwM2MWriteUpdateRequest.builder().versionedId(versionedId); + builder.value(requestBody.getValue()).timeout(this.config.getTimeout()); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteUpdateRequest(client, builder.build(), rpcCallback); + } + + private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) + .value(requestBody.getValue()) + .timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); + } + + private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MCancelObserveCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendCancelObserveRequest(client, downlink, rpcCallback); + } + + private void sendDeleteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MDeleteRequest downlink = TbLwM2MDeleteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MDeleteCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendDeleteRequest(client, downlink, rpcCallback); } - private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { - String id = getIdFromParameters(client, rpcRequst); - TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(id).timeout(this.config.getTimeout()).build(); - downlinkHandler.sendReadRequest(client, request, new TbLwM2MReadCallback(uplinkHandler, client, id)); + private void sendCancelAllObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MCancelAllRequest downlink = TbLwM2MCancelAllRequest.builder().timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MCancelAllObserveCallback(uplinkHandler, client); + var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendCancelAllRequest(client, downlink, rpcCallback); } private String getIdFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java new file mode 100644 index 0000000000..16b743101b --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LwM2MRpcResponseBody { + + private String result; + private String value; + private String error; + private String info; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java new file mode 100644 index 0000000000..8fafa294ee --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcCancelObserveCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcCancelObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(Integer response) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(response.toString()).build()); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java new file mode 100644 index 0000000000..54c621d8f9 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.jetbrains.annotations.NotNull; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback { + + public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + protected Optional serializeSuccessfulResponse(DiscoverResponse response) { + return Optional.of(Link.serialize(response.getObjectLinks())); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java new file mode 100644 index 0000000000..050e265b34 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; + +public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkRequestCallback { + + private final TransportService transportService; + private final TransportProtos.ToDeviceRpcRequestMsg request; + private final DownlinkRequestCallback callback; + + protected final LwM2mClient client; + protected final LwM2mValueConverter converter; + + public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + this.transportService = transportService; + this.client = client; + this.request = requestMsg; + this.callback = callback; + this.converter = LwM2mValueConverterImpl.getInstance(); + } + + @Override + public void onSuccess(T response) { + sendRpcReplyOnSuccess(response); + if (callback != null) { + callback.onSuccess(response); + } + } + + @Override + public void onValidationError(String msg) { + sendRpcReplyOnValidationError(msg); + if (callback != null) { + callback.onValidationError(msg); + } + } + + @Override + public void onError(Exception e) { + sendRpcReplyOnError(e); + if (callback != null) { + callback.onError(e); + } + } + + protected void reply(LwM2MRpcResponseBody response) { + TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() + .setPayload(JacksonUtil.toString(response)) + .setRequestId(request.getRequestId()) + .build(); + transportService.process(client.getSession(), msg, null); + } + + abstract protected void sendRpcReplyOnSuccess(T response); + + protected void sendRpcReplyOnValidationError(String msg) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.BAD_REQUEST.getName()).error(msg).build()); + } + + protected void sendRpcReplyOnError(Exception e) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(e.getMessage()).build()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java new file mode 100644 index 0000000000..a5f075b258 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcEmptyResponseCallback extends RpcLwM2MDownlinkCallback { + + public RpcEmptyResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + protected Optional serializeSuccessfulResponse(T response) { + return Optional.empty(); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java new file mode 100644 index 0000000000..bcb5b2ead5 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; +import java.util.Set; + +public class RpcLinkSetCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcLinkSetCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(T response) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(JacksonUtil.toString(response)).build()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java new file mode 100644 index 0000000000..c8d32ef184 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public abstract class RpcLwM2MDownlinkCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcLwM2MDownlinkCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(T response) { + LwM2MRpcResponseBody.LwM2MRpcResponseBodyBuilder builder = LwM2MRpcResponseBody.builder().result(response.getCode().getName()); + if (response.isSuccess()) { + Optional responseValue = serializeSuccessfulResponse(response); + if (responseValue.isPresent() && StringUtils.isNotEmpty(responseValue.get())) { + builder.value(responseValue.get()); + } + } else { + if (StringUtils.isNotEmpty(response.getErrorMessage())) { + builder.error(response.getErrorMessage()); + } + } + reply(builder.build()); + } + + protected abstract Optional serializeSuccessfulResponse(T response); +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java new file mode 100644 index 0000000000..6a4ed06d6b --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcReadResponseCallback extends RpcLwM2MDownlinkCallback { + + private final String versionedId; + + public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + this.versionedId = versionedId; + } + + @Override + protected Optional serializeSuccessfulResponse(T response) { + Object value = null; + if (response.getContent() instanceof LwM2mObject) { + value = client.objectToString((LwM2mObject) response.getContent(), this.converter, versionedId); + } else if (response.getContent() instanceof LwM2mObjectInstance) { + value = client.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, versionedId); + } else if (response.getContent() instanceof LwM2mResource) { + value = client.resourceToString((LwM2mResource) response.getContent(), this.converter, versionedId); + } + return Optional.of(String.format("%s", value)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java new file mode 100644 index 0000000000..67642078c6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteAttributesRequest extends IdOrKeyRequest { + + private ObjectAttributes attributes; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java new file mode 100644 index 0000000000..55fb2b49aa --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteReplaceRequest extends IdOrKeyRequest { + + private Object value; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java new file mode 100644 index 0000000000..b56fea7eb6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteUpdateRequest extends IdOrKeyRequest { + + private Object value; + private String contentFormat; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 084af742ca..5187c4c493 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -80,7 +80,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallbac import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; @@ -115,7 +115,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -333,29 +332,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.updateResourcesValue(registration, lwM2mResource, path); } } - if (rpcRequest != null) { - //TODO: move this to separate callback. - this.sendRpcRequestAfterReadResponse(registration, lwM2MClient, path, response, rpcRequest); - } - } - } - - private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response, - LwM2mClientRpcRequest rpcRequest) { - Object value = null; - if (response.getContent() instanceof LwM2mObject) { - value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer); - } else if (response.getContent() instanceof LwM2mObjectInstance) { - value = lwM2MClient.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, pathIdVer); - } else if (response.getContent() instanceof LwM2mResource) { - value = lwM2MClient.resourceToString((LwM2mResource) response.getContent(), this.converter, pathIdVer); } - String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO, - READ, pathIdVer, value); - this.sendLogsToThingsboard(lwM2MClient, msg); - rpcRequest.setValueMsg(String.format("%s", value)); - //TODO: refactor -// this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); } /** @@ -964,7 +941,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteReplaceCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(this, lwM2MClient, versionedId)); } else { log.error("Failed update resource [{}] [{}]", versionedId, newValue); String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", From 6d025d6d5e0edc6274bafc5a974491afe732e200 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 15 Jun 2021 13:45:36 +0300 Subject: [PATCH 041/151] Logging using callbacks --- .../secure/LwM2MBootstrapSecurityStore.java | 22 +- .../lwm2m/server/LwM2mServerListener.java | 13 +- .../lwm2m/server/LwM2mTransportUtil.java | 10 +- .../lwm2m/server/client/LwM2mClient.java | 7 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 29 ++- .../AbstractTbLwM2MRequestCallback.java | 18 +- .../DefaultLwM2mDownlinkMsgHandler.java | 207 ++---------------- .../downlink/DownlinkRequestCallback.java | 19 +- .../downlink/LwM2mDownlinkMsgHandler.java | 31 ++- .../TbLwM2MCancelAllObserveCallback.java | 8 +- .../TbLwM2MCancelObserveCallback.java | 8 +- .../downlink/TbLwM2MDeleteCallback.java | 15 +- .../downlink/TbLwM2MDiscoverCallback.java | 13 +- .../downlink/TbLwM2MExecuteCallback.java | 5 +- .../downlink/TbLwM2MObserveCallback.java | 18 +- .../server/downlink/TbLwM2MReadCallback.java | 14 +- .../downlink/TbLwM2MTargetedCallback.java | 44 ++++ .../TbLwM2MWriteAttributesCallback.java | 5 +- .../TbLwM2MWriteResponseCallback.java | 7 +- .../rpc/DefaultLwM2MRpcRequestHandler.java | 18 +- .../rpc/RpcCancelAllObserveCallback.java | 36 +++ .../server/rpc/RpcCancelObserveCallback.java | 5 +- .../lwm2m/server/rpc/RpcDiscoverCallback.java | 5 +- .../rpc/RpcDownlinkRequestCallbackProxy.java | 18 +- .../server/rpc/RpcEmptyResponseCallback.java | 7 +- .../lwm2m/server/rpc/RpcLinkSetCallback.java | 4 +- .../server/rpc/RpcLwM2MDownlinkCallback.java | 5 +- .../server/rpc/RpcReadResponseCallback.java | 5 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 81 +++---- .../server/uplink/LwM2mUplinkMsgHandler.java | 7 +- 30 files changed, 299 insertions(+), 385 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelAllObserveCallback.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index c23cc103df..310d738757 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.util.Hex; @@ -45,12 +42,9 @@ import java.util.Collections; import java.util.Iterator; import java.util.UUID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_SERVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SERVERS; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; @Slf4j @@ -165,14 +159,14 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); - String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint()); - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); return lwM2MBootstrapConfig; } else { log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); - log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); - String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); return null; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 97649603a6..0d566974d2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -27,7 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle import java.util.Collection; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @Slf4j @@ -87,9 +87,7 @@ public class LwM2mServerListener { @Override public void cancelled(Observation observation) { - String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath()); - service.sendLogsToThingsboard(observation.getRegistrationId(), msg); - log.warn(msg); + log.trace("Canceled Observation {}.", observation.getPath()); } @Override @@ -102,15 +100,12 @@ public class LwM2mServerListener { @Override public void onError(Observation observation, Registration registration, Exception error) { - log.error(String.format("Unable to handle notification of [%s:%s]", observation.getRegistrationId(), observation.getPath()), error); + log.error("Unable to handle notification of [{}:{}]", observation.getRegistrationId(), observation.getPath(), error); } @Override public void newObservation(Observation observation, Registration registration) { - String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, - observation.getPath()); - log.warn(msg); - service.sendLogsToThingsboard(registration.getId(), msg); + log.trace("Successful start newObservation {}.", observation.getPath()); } }; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index be5fea6df6..0d8b96e74b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -111,11 +111,11 @@ public class LwM2mTransportUtil { public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms - public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; - public static final String LOG_LW2M_INFO = "info"; - public static final String LOG_LW2M_ERROR = "error"; - public static final String LOG_LW2M_WARN = "warn"; - public static final String LOG_LW2M_VALUE = "value"; + public static final String LOG_LWM2M_TELEMETRY = "logLwm2m"; + public static final String LOG_LWM2M_INFO = "info"; + public static final String LOG_LWM2M_ERROR = "error"; + public static final String LOG_LWM2M_WARN = "warn"; + public static final String LOG_LWM2M_VALUE = "value"; public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; public static final String LWM2M_VERSION_DEFAULT = "1.0"; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 77454d8b83..80e85036c3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -20,7 +20,6 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; @@ -38,8 +37,8 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Collection; @@ -56,13 +55,11 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; -import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; @Slf4j diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 6896c3d8de..000e97ccc1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -61,8 +61,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; @@ -124,7 +124,6 @@ public class LwM2mFwSwUpdate { @Getter LwM2mClient lwM2MClient; @Getter - @Setter private final List pendingInfoRequestsStart; @Getter @Setter @@ -189,11 +188,11 @@ public class LwM2mFwSwUpdate { public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); - String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, + String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO, LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId); - handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); + handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { int chunkSize = 0; @@ -220,7 +219,7 @@ public class LwM2mFwSwUpdate { // handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); } log.error(msgError); - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_ERROR, msgError); } } @@ -228,10 +227,10 @@ public class LwM2mFwSwUpdate { // this.sendSateOnThingsBoard(handler); String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); - if (LOG_LW2M_ERROR.equals(typeInfo)) { + if (LOG_LWM2M_ERROR.equals(typeInfo)) { msg = String.format("%s Error: %s", msg, msgError); } - handler.sendLogsToThingsboard(lwM2MClient, msg); + handler.logToTelemetry(lwM2MClient, msg); } @@ -241,7 +240,7 @@ public class LwM2mFwSwUpdate { * send execute */ public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); + this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); //TODO: user this.rpcRequest??? TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build(); request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteCallback(handler, lwM2MClient, pathInstallId)); @@ -272,14 +271,14 @@ public class LwM2mFwSwUpdate { ver3 != null && ver3.contains(this.currentVersion) )) || (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { - fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LW2M_ERROR, + fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR, this.currentVersion); } else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { - fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LW2M_ERROR, + fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR, updateResultFw); } if (fwMsg != null) { - handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); + handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); return false; } else { return true; @@ -364,10 +363,10 @@ public class LwM2mFwSwUpdate { String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); + this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); } else { this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value); + this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_ERROR, value); } handler.helper.sendParametersOnThingsboardTelemetry( handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java index 66da1c3d38..764d5ff5ef 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java @@ -15,10 +15,14 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; -public abstract class AbstractTbLwM2MRequestCallback implements DownlinkRequestCallback { +@Slf4j +public abstract class AbstractTbLwM2MRequestCallback implements DownlinkRequestCallback { protected final LwM2mUplinkMsgHandler handler; protected final LwM2mClient client; @@ -29,12 +33,14 @@ public abstract class AbstractTbLwM2MRequestCallback implements DownlinkReque } @Override - public void onValidationError(String msg) { - + public void onValidationError(String params, String msg) { + log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg); + handler.logToTelemetry(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg)); } @Override - public void onError(Exception e) { - + public void onError(String params, Exception e) { + log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e); + handler.logToTelemetry(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 837e2a17bb..746b985812 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -49,6 +49,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; @@ -88,7 +89,6 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; - private final LwM2mClientContext lwM2mClientContext; @PostConstruct public void init() { @@ -98,14 +98,14 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback) { + public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback) { validateVersionedId(client, request); ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); sendRequest(client, downlink, request.getTimeout(), callback); } @Override - public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { + public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { validateVersionedId(client, request); LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); @@ -127,19 +127,19 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback) { + public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback) { Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); Set paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); - callback.onSuccess(paths); + callback.onSuccess(request, paths); } @Override - public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { - callback.onSuccess(Arrays.asList(client.getRegistration().getSortedObjectLinks())); + public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { + callback.onSuccess(request, Arrays.asList(client.getRegistration().getSortedObjectLinks())); } @Override - public void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback) { + public void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback) { ResourceModel resourceModelExecute = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); if (resourceModelExecute != null) { ExecuteRequest downlink; @@ -153,30 +153,30 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback) { + public void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback) { sendRequest(client, new DeleteRequest(request.getObjectId()), request.getTimeout(), callback); } @Override - public void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback) { + public void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback) { int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration(), request.getObjectId()); - callback.onSuccess(observeCancelCnt); + callback.onSuccess(request, observeCancelCnt); } @Override - public void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback) { + public void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback) { int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration()); - callback.onSuccess(observeCancelCnt); + callback.onSuccess(request, observeCancelCnt); } @Override - public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback) { + public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback) { validateVersionedId(client, request); sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); } @Override - public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback) { + public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback) { validateVersionedId(client, request); if (request.getAttributes() == null) { throw new IllegalArgumentException("Attributes to write are not specified!"); @@ -196,7 +196,7 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback) { + public void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback) { ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); if (resourceModelWrite != null) { ContentFormat contentFormat = convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); @@ -206,15 +206,15 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), request.getValue()); sendRequest(client, downlink, request.getTimeout(), callback); } catch (Exception e) { - callback.onError(e); + callback.onError(JacksonUtil.toString(request), e); } } else { - //TODO: log validation error using callback. + callback.onValidationError(JacksonUtil.toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); } } @Override - public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { + public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); if (resultIds.isResource()) { /* @@ -238,186 +238,27 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); sendRequest(client, downlink, request.getTimeout(), callback); } else { - callback.onValidationError("No resources to update!"); + callback.onValidationError(JacksonUtil.toString(request), "No resources to update!"); } } else { - callback.onValidationError("Update of the root level object is not supported yet!"); + callback.onValidationError(JacksonUtil.toString(request), "Update of the root level object is not supported yet!"); } } -// public void sendAllRequest(LwM2mClient client, String targetIdVer, LwM2mTypeOper typeOper, -// ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { -// Registration registration = client.getRegistration(); -// try { -// String target = fromVersionedIdToObjectId(targetIdVer); -// if (contentFormat == null) { -// contentFormat = client.getDefaultContentFormat(); -// } -// LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; -// if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0) { -// if (client.isValidObjectVersion(targetIdVer)) { -// timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; -// SimpleDownlinkRequest request = createRequest(registration, client, typeOper, contentFormat, target, -// targetIdVer, resultIds, params, lwm2mClientRpcRequest); -// if (request != null) { -// try { -// this.sendRequest(client, request, timeoutInMs, lwm2mClientRpcRequest); -// } catch (ClientSleepingException e) { -// SimpleDownlinkRequest finalRequest = request; -// long finalTimeoutInMs = timeoutInMs; -// LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; -// client.getQueuedRequests().add(() -> sendRequest(client, finalRequest, finalTimeoutInMs, finalRpcRequest)); -// } catch (Exception e) { -// log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); -// } -// } else if (WRITE_UPDATE.name().equals(typeOper.name())) { -// if (lwm2mClientRpcRequest != null) { -// String errorMsg = String.format("Path %s params is not valid", targetIdVer); -// handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { -// if (lwm2mClientRpcRequest != null) { -// String errorMsg = String.format("Path %s object model is absent", targetIdVer); -// handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { -// log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); -// if (lwm2mClientRpcRequest != null) { -// ResourceModel resourceModel = client.getResourceModel(targetIdVer, this.config.getModelProvider()); -// String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; -// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// } -// } else if (lwm2mClientRpcRequest != null) { -// String errorMsg = String.format("Path %s not found in object version", targetIdVer); -// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// } else { -// switch (typeOper) { -// case OBSERVE_READ_ALL: -// case DISCOVER_ALL: -// Set paths; -// if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { -// Set observations = context.getServer().getObservationService().getObservations(registration); -// paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); -// } else { -// assert registration != null; -// Link[] objectLinks = registration.getSortedObjectLinks(); -// paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); -// } -// String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, -// typeOper.name(), paths); -// this.handler.sendLogsToThingsboard(client, msg); -// if (lwm2mClientRpcRequest != null) { -// String valueMsg = String.format("Paths - %s", paths); -// handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); -// } -// break; -// case OBSERVE_CANCEL: -// case OBSERVE_CANCEL_ALL: -// int observeCancelCnt = 0; -// String observeCancelMsg = null; -// if (OBSERVE_CANCEL.name().equals(typeOper)) { -// observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); -// observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, -// OBSERVE_CANCEL.name(), target, observeCancelCnt); -// } else { -// observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); -// observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, -// OBSERVE_CANCEL.name(), observeCancelCnt); -// } -// this.afterObserveCancel(client, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); -// break; -// // lwm2mClientRpcRequest != null -// case FW_UPDATE: -// handler.getInfoFirmwareUpdate(client, lwm2mClientRpcRequest); -// break; -// } -// } -// } catch (Exception e) { -// String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, -// typeOper.name(), e.getMessage()); -// handler.sendLogsToThingsboard(client, msg); -// if (lwm2mClientRpcRequest != null) { -// String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); -// handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// } -// } - - private void sendRequest(LwM2mClient client, SimpleDownlinkRequest request, long timeoutInMs, DownlinkRequestCallback callback) { + private , T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback callback) { Registration registration = client.getRegistration(); context.getServer().send(registration, request, timeoutInMs, response -> { -// if (!client.isInit()) { -// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); -// } responseRequestExecutor.submit(() -> { try { - callback.onSuccess(response); + callback.onSuccess(request, response); } catch (Exception e) { log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); } }); -// if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { -// this.handleResponse(client, request.getPath().toString(), response, request, rpcRequest); -// } else { -// String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), -// ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); -// handler.sendLogsToThingsboard(client, msg); -// log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), -// ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); -// if (!client.isInit()) { -// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); -// } -// /** Not Found */ -// if (rpcRequest != null) { -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); -// } -// /** Not Found -// set setClient_fw_info... = empty -// **/ -// if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { -// client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); -// } -// if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { -// client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); -// } -// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { -// this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); -// } -// if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { -// this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage()); -// } -// } }, e -> { responseRequestExecutor.submit(() -> { - callback.onError(e); + callback.onError(JacksonUtil.toString(request), e); }); -// /** version == null -// set setClient_fw_info... = empty -// **/ -// if (client.getFwUpdate() != null && client.getFwUpdate().isInfoFwSwUpdate()) { -// client.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); -// } -// if (client.getSwUpdate() != null && client.getSwUpdate().isInfoFwSwUpdate()) { -// client.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); -// } -// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { -// this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); -// } -// if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { -// this.afterExecuteFwSwUpdateError(registration, request, e.getMessage()); -// } -// if (!client.isInit()) { -// client.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); -// } -// String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", -// LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); -// handler.sendLogsToThingsboard(client, msg); -// log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); -// if (rpcRequest != null) { -// handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); -// } }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java index 342abf39f4..eea4e7ac2b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DownlinkRequestCallback.java @@ -15,28 +15,29 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -public interface DownlinkRequestCallback { +public interface DownlinkRequestCallback { - void onSuccess(T response); + void onSuccess(R request, T response); - void onValidationError(String msg); + void onValidationError(String params, String msg); - void onError(Exception e); + void onError(String params, Exception e); + + static DownlinkRequestCallback doNothing() { + return new DownlinkRequestCallback<>() { - static DownlinkRequestCallback doNothing() { - return new DownlinkRequestCallback() { @Override - public void onSuccess(T response) { + public void onSuccess(R request, T response) { } @Override - public void onValidationError(String msg) { + public void onValidationError(String params, String msg) { } @Override - public void onError(Exception e) { + public void onError(String params, Exception e) { } }; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java index 724dc78993..adb2e32293 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java @@ -16,6 +16,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.request.DeleteRequest; +import org.eclipse.leshan.core.request.DiscoverRequest; +import org.eclipse.leshan.core.request.ExecuteRequest; +import org.eclipse.leshan.core.request.ObserveRequest; +import org.eclipse.leshan.core.request.ReadRequest; +import org.eclipse.leshan.core.request.WriteAttributesRequest; +import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ExecuteResponse; @@ -30,29 +37,29 @@ import java.util.Set; public interface LwM2mDownlinkMsgHandler { - void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback); + void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback); - void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback); + void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback); - void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback); + void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback> callback); - void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback); + void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback callback); - void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback); + void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback callback); - void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback); + void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback callback); - void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback); + void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback callback); - void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback); + void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback); - void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); + void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); - void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback); + void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback); - void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback); + void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback); - void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback); + void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index 6d58853164..42933689ae 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -18,18 +18,18 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL_ALL; -public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { public TbLwM2MCancelAllObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { super(handler, client); } @Override - public void onSuccess(Integer canceledSubscriptionsCount) { - String observeCancelMsg = String.format("%s: type operation %s paths: count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL_ALL.name(), canceledSubscriptionsCount); + public void onSuccess(TbLwM2MCancelAllRequest request, Integer canceledSubscriptionsCount) { + String observeCancelMsg = String.format("%s: type operation %s paths: count: %d", LOG_LWM2M_INFO, OBSERVE_CANCEL_ALL.name(), canceledSubscriptionsCount); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index d0fc5fdd6a..65179de20b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -18,10 +18,10 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; -public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { private final String versionedId; @@ -31,8 +31,8 @@ public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback } @Override - public void onSuccess(Integer canceledSubscriptionsCount) { - String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, OBSERVE_CANCEL.name(), versionedId, canceledSubscriptionsCount); + public void onSuccess(TbLwM2MCancelObserveRequest request, Integer canceledSubscriptionsCount) { + String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LWM2M_INFO, OBSERVE_CANCEL.name(), versionedId, canceledSubscriptionsCount); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java index 8bd2e7505f..32c82404c6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java @@ -15,22 +15,15 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.response.DeleteResponse; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -public class TbLwM2MDeleteCallback extends AbstractTbLwM2MRequestCallback { - - private final String targetId; +public class TbLwM2MDeleteCallback extends TbLwM2MTargetedCallback { public TbLwM2MDeleteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; - } - - @Override - public void onSuccess(DeleteResponse response) { - //TODO: separate callback wrapper for the RPC calls. + super(handler, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java index e21949f7c4..5ae20e064d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java @@ -15,22 +15,15 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.response.DiscoverResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MDiscoverCallback extends AbstractTbLwM2MRequestCallback { - - private final String targetId; +public class TbLwM2MDiscoverCallback extends TbLwM2MTargetedCallback { public TbLwM2MDiscoverCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; - } - - @Override - public void onSuccess(DiscoverResponse response) { - //TODO: separate callback wrapper for the RPC calls. + super(handler, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java index ff7c40b3b9..5d01e4267e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java @@ -15,11 +15,12 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.response.ExecuteResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; @@ -29,7 +30,7 @@ public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; - private final String targetId; +@Slf4j +public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback { public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; + super(handler, client, targetId); } @Override - public void onSuccess(ObserveResponse response) { - //TODO: handle the response (at least log to telemetry) + public void onSuccess(ObserveRequest request, ObserveResponse response) { + super.onSuccess(request, response); + handler.onUpdateValueAfterReadResponse(client.getRegistration(), targetId, response, null); } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java index 9ab30a9410..b6d0801f40 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java @@ -15,22 +15,24 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MReadCallback extends AbstractTbLwM2MRequestCallback { +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; - private final String targetId; +@Slf4j +public class TbLwM2MReadCallback extends TbLwM2MTargetedCallback { public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; + super(handler, client, targetId); } @Override - public void onSuccess(ReadResponse response) { - //TODO: separate callback wrapper for the RPC calls. + public void onSuccess(ReadRequest request, ReadResponse response) { + super.onSuccess(request, response); handler.onUpdateValueAfterReadResponse(client.getRegistration(), targetId, response, null); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java new file mode 100644 index 0000000000..e331abec11 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.request.ReadRequest; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; + +@Slf4j +public abstract class TbLwM2MTargetedCallback extends AbstractTbLwM2MRequestCallback { + + protected final String targetId; + + public TbLwM2MTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + super(handler, client); + this.targetId = targetId; + } + + @Override + public void onSuccess(R request, T response) { + //TODO convert camelCase to "camel case" using .split("(? { +public class TbLwM2MWriteAttributesCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; @@ -29,7 +30,7 @@ public class TbLwM2MWriteAttributesCallback extends AbstractTbLwM2MRequestCallba } @Override - public void onSuccess(WriteAttributesResponse response) { + public void onSuccess(WriteAttributesRequest request, WriteAttributesResponse response) { //TODO: separate callback wrapper for the RPC calls. } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index 2cee4887c9..b383239777 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -15,11 +15,13 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import lombok.Setter; +import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; @@ -29,7 +31,8 @@ public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback } @Override - public void onSuccess(WriteResponse response) { + public void onSuccess(WriteRequest request, WriteResponse response) { + handler.onWriteResponseOk(client, targetId, request); //TODO: separate callback wrapper for the RPC calls. } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 0c371dac4e..80dad717aa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -59,9 +59,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_VALUE; @Slf4j @Service @@ -167,12 +167,12 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private void sendObserveAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { TbLwM2MObserveAllRequest request = TbLwM2MObserveAllRequest.builder().timeout(this.config.getTimeout()).build(); - downlinkHandler.sendObserveAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + downlinkHandler.sendObserveAllRequest(client, request, new RpcLinkSetCallback<>(transportService, client, requestMsg, null)); } private void sendDiscoverAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { TbLwM2MDiscoverAllRequest request = TbLwM2MDiscoverAllRequest.builder().timeout(this.config.getTimeout()).build(); - downlinkHandler.sendDiscoverAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + downlinkHandler.sendDiscoverAllRequest(client, request, new RpcLinkSetCallback<>(transportService, client, requestMsg, null)); } private void sendDiscoverRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { @@ -235,7 +235,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private void sendCancelAllObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { TbLwM2MCancelAllRequest downlink = TbLwM2MCancelAllRequest.builder().timeout(this.config.getTimeout()).build(); var mainCallback = new TbLwM2MCancelAllObserveCallback(uplinkHandler, client); - var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); + var rpcCallback = new RpcCancelAllObserveCallback(transportService, client, requestMsg, mainCallback); downlinkHandler.sendCancelAllRequest(client, downlink, rpcCallback); } @@ -283,18 +283,18 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { rpcRequest.setResponseCode(requestCode); - if (LOG_LW2M_ERROR.equals(typeMsg)) { + if (LOG_LWM2M_ERROR.equals(typeMsg)) { rpcRequest.setInfoMsg(null); rpcRequest.setValueMsg(null); if (rpcRequest.getErrorMsg() == null) { msg = msg.isEmpty() ? null : msg; rpcRequest.setErrorMsg(msg); } - } else if (LOG_LW2M_INFO.equals(typeMsg)) { + } else if (LOG_LWM2M_INFO.equals(typeMsg)) { if (rpcRequest.getInfoMsg() == null) { rpcRequest.setInfoMsg(msg); } - } else if (LOG_LW2M_VALUE.equals(typeMsg)) { + } else if (LOG_LWM2M_VALUE.equals(typeMsg)) { if (rpcRequest.getValueMsg() == null) { rpcRequest.setValueMsg(msg); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelAllObserveCallback.java new file mode 100644 index 0000000000..d2b7ffb834 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelAllObserveCallback.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.ResponseCode; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; + +public class RpcCancelAllObserveCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcCancelAllObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(Integer response) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(response.toString()).build()); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java index 8fafa294ee..1d1f1de230 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java @@ -25,12 +25,13 @@ import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import java.util.Optional; -public class RpcCancelObserveCallback extends RpcDownlinkRequestCallbackProxy { +public class RpcCancelObserveCallback extends RpcDownlinkRequestCallbackProxy { - public RpcCancelObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcCancelObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java index 54c621d8f9..ba8a634de0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java @@ -19,6 +19,7 @@ import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ReadResponse; import org.jetbrains.annotations.NotNull; @@ -30,9 +31,9 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; -public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback { +public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback { - public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java index 050e265b34..115680b50f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java @@ -24,16 +24,16 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; -public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkRequestCallback { +public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkRequestCallback { private final TransportService transportService; private final TransportProtos.ToDeviceRpcRequestMsg request; - private final DownlinkRequestCallback callback; + private final DownlinkRequestCallback callback; protected final LwM2mClient client; protected final LwM2mValueConverter converter; - public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { this.transportService = transportService; this.client = client; this.request = requestMsg; @@ -42,26 +42,26 @@ public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkRequ } @Override - public void onSuccess(T response) { + public void onSuccess(R request, T response) { sendRpcReplyOnSuccess(response); if (callback != null) { - callback.onSuccess(response); + callback.onSuccess(request, response); } } @Override - public void onValidationError(String msg) { + public void onValidationError(String params, String msg) { sendRpcReplyOnValidationError(msg); if (callback != null) { - callback.onValidationError(msg); + callback.onValidationError(params, msg); } } @Override - public void onError(Exception e) { + public void onError(String params, Exception e) { sendRpcReplyOnError(e); if (callback != null) { - callback.onError(e); + callback.onError(params, e); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java index a5f075b258..5bb2a8550f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java @@ -15,8 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.rpc; -import org.eclipse.leshan.core.Link; -import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.request.LwM2mRequest; import org.eclipse.leshan.core.response.LwM2mResponse; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -25,9 +24,9 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; -public class RpcEmptyResponseCallback extends RpcLwM2MDownlinkCallback { +public class RpcEmptyResponseCallback, T extends LwM2mResponse> extends RpcLwM2MDownlinkCallback { - public RpcEmptyResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcEmptyResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java index bcb5b2ead5..82d880c206 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java @@ -27,9 +27,9 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; import java.util.Set; -public class RpcLinkSetCallback extends RpcDownlinkRequestCallbackProxy { +public class RpcLinkSetCallback extends RpcDownlinkRequestCallbackProxy { - public RpcLinkSetCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcLinkSetCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java index c8d32ef184..70536d1be8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.rpc; +import org.eclipse.leshan.core.request.LwM2mRequest; import org.eclipse.leshan.core.response.LwM2mResponse; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.transport.TransportService; @@ -24,9 +25,9 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; -public abstract class RpcLwM2MDownlinkCallback extends RpcDownlinkRequestCallbackProxy { +public abstract class RpcLwM2MDownlinkCallback, T extends LwM2mResponse> extends RpcDownlinkRequestCallbackProxy { - public RpcLwM2MDownlinkCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + public RpcLwM2MDownlinkCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java index 6a4ed06d6b..d3da6e6a3f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server.rpc; import org.eclipse.leshan.core.node.LwM2mObject; import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.request.LwM2mRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -26,11 +27,11 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; -public class RpcReadResponseCallback extends RpcLwM2MDownlinkCallback { +public class RpcReadResponseCallback, T extends ReadResponse> extends RpcLwM2MDownlinkCallback { private final String versionedId; - public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback callback) { + public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback callback) { super(transportService, client, requestMsg, callback); this.versionedId = versionedId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 5187c4c493..15df144cc4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -111,10 +111,10 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.D import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -196,7 +196,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); if (lwM2MClient != null) { this.clientContext.register(lwM2MClient, registration); - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId()); + this.logToTelemetry(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); SessionInfoProto sessionInfo = lwM2MClient.getSession(); transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); @@ -219,11 +219,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { // Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again. onRegistered(registration, previousObservations); } else { - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); + this.logToTelemetry(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); } } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to: " + t.getMessage()); + this.logToTelemetry(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to: " + t.getMessage()); } }); } @@ -255,7 +255,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); + this.logToTelemetry(lwM2MClient, LOG_LWM2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); } }); } @@ -268,7 +268,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { unRegistrationExecutor.submit(() -> { LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); try { - this.sendLogsToThingsboard(client, LOG_LW2M_INFO + ": Client unRegistration"); + this.logToTelemetry(client, LOG_LWM2M_INFO + ": Client unRegistration"); clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { @@ -283,7 +283,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); - this.sendLogsToThingsboard(client, LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); + this.logToTelemetry(client, LOG_LWM2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); } }); } @@ -291,7 +291,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { @Override public void onSleepingDev(Registration registration) { log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); - this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is sleeping!"); + this.logToTelemetry(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is sleeping!"); //TODO: associate endpointId with device information. } @@ -323,13 +323,13 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (objectModelVersion != null) { if (response.getContent() instanceof LwM2mObject) { LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); - this.updateObjectResourceValue(registration, lwM2mObject, path); + this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path); } else if (response.getContent() instanceof LwM2mObjectInstance) { LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent(); - this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path); + this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path); } else if (response.getContent() instanceof LwM2mResource) { LwM2mResource lwM2mResource = (LwM2mResource) response.getContent(); - this.updateResourcesValue(registration, lwM2mResource, path); + this.updateResourcesValue(lwM2MClient, lwM2mResource, path); } } } @@ -374,14 +374,14 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } else { log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", - LOG_LW2M_ERROR, pathIdVer, valueNew); - this.sendLogsToThingsboard(lwM2MClient, logMsg); + LOG_LWM2M_ERROR, pathIdVer, valueNew); + this.logToTelemetry(lwM2MClient, logMsg); } } else if (!isFwSwWords(pathName)) { log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", - LOG_LW2M_ERROR, pathName, valueNew); - this.sendLogsToThingsboard(lwM2MClient, logMsg); + LOG_LWM2M_ERROR, pathName, valueNew); + this.logToTelemetry(lwM2MClient, logMsg); } }); @@ -454,7 +454,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { @Override public void onAwakeDev(Registration registration) { log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); - this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is awake!"); + this.logToTelemetry(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is awake!"); //TODO: associate endpointId with device information. } @@ -463,17 +463,17 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param registrationId - Id of Registration LwM2M Client */ @Override - public void sendLogsToThingsboard(String registrationId, String logMsg) { - sendLogsToThingsboard(clientContext.getClientByRegistrationId(registrationId), logMsg); + public void logToTelemetry(String registrationId, String logMsg) { + logToTelemetry(clientContext.getClientByRegistrationId(registrationId), logMsg); } @Override - public void sendLogsToThingsboard(LwM2mClient client, String logMsg) { + public void logToTelemetry(LwM2mClient client, String logMsg) { if (logMsg != null && client != null && client.getSession() != null) { if (logMsg.length() > 1024) { logMsg = logMsg.substring(0, 1024); } - this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), client.getSession()); + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession()); } } @@ -563,29 +563,19 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(this, client, versionedId)); } - /** - * @param registration - - * @param lwM2mObject - - * @param pathIdVer - - */ - private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String pathIdVer) { + private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObject.getInstances().forEach((instanceId, instance) -> { String pathInstance = pathIds.toString() + "/" + instanceId; - this.updateObjectInstanceResourceValue(registration, instance, pathInstance); + this.updateObjectInstanceResourceValue(client, instance, pathInstance); }); } - /** - * @param registration - - * @param lwM2mObjectInstance - - * @param pathIdVer - - */ - private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { + private void updateObjectInstanceResourceValue(LwM2mClient client, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { String pathRez = pathIds.toString() + "/" + resourceId; - this.updateResourcesValue(registration, resource, pathRez); + this.updateResourcesValue(client, resource, pathRez); }); } @@ -596,12 +586,12 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * #3 If fr_update -> UpdateFirmware * #4 updateAttrTelemetry * - * @param registration - Registration LwM2M Client + * @param lwM2MClient - Registration LwM2M Client * @param lwM2mResource - LwM2mSingleResource response.getContent() * @param path - resource */ - private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { - LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); + private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path) { + Registration registration = lwM2MClient.getRegistration(); if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { /** version != null * set setClient_fw_info... = value @@ -801,12 +791,13 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param path - * @param request - */ - public void onWriteResponseOk(Registration registration, String path, WriteRequest request) { + @Override + public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { if (request.getNode() instanceof LwM2mResource) { - this.updateResourcesValue(registration, ((LwM2mResource) request.getNode()), path); + this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path); } else if (request.getNode() instanceof LwM2mObjectInstance) { ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> { - this.updateResourcesValue(registration, resource, path + "/" + resId); + this.updateResourcesValue(client, resource, path + "/" + resId); }); } @@ -945,8 +936,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } else { log.error("Failed update resource [{}] [{}]", versionedId, newValue); String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", - LOG_LW2M_ERROR, versionedId, newValue); - this.sendLogsToThingsboard(lwM2MClient, logMsg); + LOG_LWM2M_ERROR, versionedId, newValue); + this.logToTelemetry(lwM2MClient, logMsg); log.info("Failed update resource [{}] [{}]", versionedId, newValue); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 5d273abde7..f29deb1cf3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.uplink; import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.Device; @@ -54,9 +55,11 @@ public interface LwM2mUplinkMsgHandler { void onAwakeDev(Registration registration); - void sendLogsToThingsboard(LwM2mClient client, String msg); + void logToTelemetry(LwM2mClient client, String msg); - void sendLogsToThingsboard(String registrationId, String msg); + void logToTelemetry(String registrationId, String msg); + + void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request); void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); From 2c31de4a2c3ae2f53c45eecff5abcb848a56e596 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 15 Jun 2021 14:49:45 +0300 Subject: [PATCH 042/151] Fix duplication of sessions --- .../server/client/LwM2mClientContext.java | 4 +-- .../server/client/LwM2mClientContextImpl.java | 5 +++- .../TbLwM2MCancelAllObserveCallback.java | 8 ++++-- .../TbLwM2MCancelObserveCallback.java | 5 +++- .../downlink/TbLwM2MExecuteCallback.java | 12 ++------ .../downlink/TbLwM2MObserveCallback.java | 6 +--- .../server/downlink/TbLwM2MReadCallback.java | 4 +-- .../downlink/TbLwM2MTargetedCallback.java | 12 ++++---- .../TbLwM2MWriteAttributesCallback.java | 14 ++-------- .../TbLwM2MWriteResponseCallback.java | 14 ++++------ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 28 +++++++++---------- 11 files changed, 46 insertions(+), 66 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index f0539a0ccc..0f2f208b2b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -18,11 +18,11 @@ package org.thingsboard.server.transport.lwm2m.server.client; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Collection; +import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -34,7 +34,7 @@ public interface LwM2mClientContext { LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo); - void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException; + Optional register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException; void updateRegistration(LwM2mClient client, Registration registration) throws LwM2MClientStateException; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index c063a6f359..b9206ca4df 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -60,12 +60,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } @Override - public void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException { + public Optional register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException { + TransportProtos.SessionInfoProto oldSession = null; lwM2MClient.lock(); try { if (LwM2MClientState.UNREGISTERED.equals(lwM2MClient.getState())) { throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state."); } + oldSession = lwM2MClient.getSession(); TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); if (securityInfo.getSecurityMode() != null) { if (securityInfo.getDeviceProfile() != null) { @@ -89,6 +91,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } finally { lwM2MClient.unlock(); } + return Optional.ofNullable(oldSession); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index 42933689ae..42f2a1baec 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -15,12 +15,13 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL_ALL; +@Slf4j public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { public TbLwM2MCancelAllObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { @@ -29,7 +30,8 @@ public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallb @Override public void onSuccess(TbLwM2MCancelAllRequest request, Integer canceledSubscriptionsCount) { - String observeCancelMsg = String.format("%s: type operation %s paths: count: %d", LOG_LWM2M_INFO, OBSERVE_CANCEL_ALL.name(), canceledSubscriptionsCount); + log.trace("[{}] Cancel of all observations was successful: {}", client.getEndpoint(), canceledSubscriptionsCount); + handler.logToTelemetry(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index 65179de20b..eec33963d5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; +@Slf4j public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback { private final String versionedId; @@ -32,7 +34,8 @@ public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback @Override public void onSuccess(TbLwM2MCancelObserveRequest request, Integer canceledSubscriptionsCount) { - String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LWM2M_INFO, OBSERVE_CANCEL.name(), versionedId, canceledSubscriptionsCount); + log.trace("[{}] Cancel observation of [{}] successful: {}", client.getEndpoint(), versionedId, canceledSubscriptionsCount); + handler.logToTelemetry(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java index 5d01e4267e..d2aed6a4fd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java @@ -20,18 +20,10 @@ import org.eclipse.leshan.core.response.ExecuteResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MExecuteCallback extends AbstractTbLwM2MRequestCallback { - - private final String targetId; +public class TbLwM2MExecuteCallback extends TbLwM2MTargetedCallback { public TbLwM2MExecuteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; - } - - @Override - public void onSuccess(ExecuteRequest request, ExecuteResponse response) { - //TODO: separate callback wrapper for the RPC calls. + super(handler, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java index 890a6e6097..e5453902fb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java @@ -17,14 +17,10 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.ObserveRequest; -import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; - @Slf4j public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback { @@ -35,6 +31,6 @@ public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback { @@ -33,7 +31,7 @@ public class TbLwM2MReadCallback extends TbLwM2MTargetedCallback extends AbstractTbLwM2MRequestCallback { - protected final String targetId; + protected final String versionedId; - public TbLwM2MTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { super(handler, client); - this.targetId = targetId; + this.versionedId = versionedId; } @Override public void onSuccess(R request, T response) { //TODO convert camelCase to "camel case" using .split("(? { - - private final String targetId; +public class TbLwM2MWriteAttributesCallback extends TbLwM2MTargetedCallback { public TbLwM2MWriteAttributesCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; - } - - @Override - public void onSuccess(WriteAttributesRequest request, WriteAttributesResponse response) { - //TODO: separate callback wrapper for the RPC calls. + super(handler, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index b383239777..89586bd113 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -15,25 +15,21 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; -import lombok.Setter; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.WriteResponse; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback { - - private final String targetId; +public class TbLwM2MWriteResponseCallback extends TbLwM2MTargetedCallback { public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client); - this.targetId = targetId; + super(handler, client, targetId); } @Override public void onSuccess(WriteRequest request, WriteResponse response) { - handler.onWriteResponseOk(client, targetId, request); - //TODO: separate callback wrapper for the RPC calls. + super.onSuccess(request, response); + handler.onWriteResponseOk(client, versionedId, request); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 15df144cc4..34163815d7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -195,7 +195,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { try { log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); if (lwM2MClient != null) { - this.clientContext.register(lwM2MClient, registration); + Optional oldSessionInfo = this.clientContext.register(lwM2MClient, registration); + if (oldSessionInfo.isPresent()) { + log.info("[{}] Closing old session: {}", registration.getEndpoint(), new UUID(oldSessionInfo.get().getSessionIdMSB(), oldSessionInfo.get().getSessionIdLSB())); + closeSession(oldSessionInfo.get()); + } this.logToTelemetry(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); SessionInfoProto sessionInfo = lwM2MClient.getSession(); transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); @@ -272,8 +276,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); - transportService.deregisterSession(sessionInfo); + closeSession(sessionInfo); sessionStore.remove(registration.getEndpoint()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { @@ -288,6 +291,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { }); } + public void closeSession(SessionInfoProto sessionInfo) { + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); + transportService.deregisterSession(sessionInfo); + } + @Override public void onSleepingDev(Registration registration) { log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); @@ -441,8 +449,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { */ @Override public void doDisconnect(SessionInfoProto sessionInfo) { - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); - transportService.deregisterSession(sessionInfo); + closeSession(sessionInfo); } /** @@ -586,7 +593,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * #3 If fr_update -> UpdateFirmware * #4 updateAttrTelemetry * - * @param lwM2MClient - Registration LwM2M Client + * @param lwM2MClient - Registration LwM2M Client * @param lwM2mResource - LwM2mSingleResource response.getContent() * @param path - resource */ @@ -784,13 +791,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return lwm2mResourceValue; } - /** - * Update resource (attribute) value on thingsboard after update value in client - * - * @param registration - - * @param path - - * @param request - - */ @Override public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { if (request.getNode() instanceof LwM2mResource) { @@ -955,7 +955,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * Get path to resource from profile equal keyName * * @param sessionInfo - - * @param keyName - + * @param keyName - * @return - */ @Override From 2d4c793b9f6566357acdc1a0a5a4b8435bcb63f2 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 15 Jun 2021 15:25:40 +0300 Subject: [PATCH 043/151] LwM2M refactoring form structure --- .../lwm2m-device-config-server.component.ts | 30 +++-- ...ile-transport-configuration.component.html | 69 +++++------ ...ofile-transport-configuration.component.ts | 116 +++++++++--------- .../lwm2m/lwm2m-profile-config.models.ts | 24 ++-- .../assets/locale/locale.constant-en_US.json | 18 ++- 5 files changed, 135 insertions(+), 122 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts index cccaed895f..f7c37c8109 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts @@ -15,7 +15,14 @@ /// import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, ValidationErrors, Validator, + Validators +} from '@angular/forms'; import { DEFAULT_PORT_BOOTSTRAP_NO_SEC, DEFAULT_PORT_SERVER_NO_SEC, @@ -40,11 +47,16 @@ import { deepClone } from '@core/utils'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Lwm2mDeviceConfigServerComponent), multi: true - } + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mDeviceConfigServerComponent), + multi: true + }, ] }) -export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, OnDestroy { +export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAccessor, Validator, OnDestroy { private disabled = false; private destroy$ = new Subject(); @@ -159,11 +171,7 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc private propagateChangeState = (value: ServerSecurityConfig): void => { if (value !== undefined) { - if (this.serverFormGroup.valid) { - this.propagateChange(value); - } else { - this.propagateChange(null); - } + this.propagateChange(value); } } @@ -198,4 +206,10 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc } return config; } + + validate(): ValidationErrors | null { + return this.serverFormGroup.valid ? null : { + serverFormGroup: true + }; + } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 30b78393d3..13e32f10b5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -34,53 +34,47 @@ - -
+
+
{{ 'device-profile.lwm2m.servers' | translate }} - +
{{ 'device-profile.lwm2m.short-id' | translate }} - - - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + + + {{ 'device-profile.lwm2m.short-id-required' | translate }} {{ 'device-profile.lwm2m.lifetime' | translate }} - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + + + {{ 'device-profile.lwm2m.lifetime-required' | translate }} {{ 'device-profile.lwm2m.default-min-period' | translate }} - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} + + + {{ 'device-profile.lwm2m.default-min-period-required' | translate }}
{{ 'device-profile.lwm2m.binding' | translate }} - - {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} + + {{ bindingModeTypeNamesMap.get(bindingMode) | translate }} - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} + {{ 'device-profile.lwm2m.notification-storing' | translate }}
@@ -88,33 +82,27 @@ {{ 'device-profile.lwm2m.bootstrap-server' | translate }} - - - - + + {{ 'device-profile.lwm2m.lwm2m-server' | translate }} - - - - + +
- +
- -
+ +
device-profile.lwm2m.fw-update @@ -128,7 +116,7 @@ {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} - + {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }} @@ -145,7 +133,7 @@ {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} - + {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }} @@ -182,6 +170,7 @@
object; isFwUpdateStrategy: boolean; isSwUpdateStrategy: boolean; @@ -90,45 +91,53 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceProfileFormGroup = this.fb.group({ objectIds: [null, Validators.required], observeAttrTelemetry: [null, Validators.required], - shortId: [null, Validators.required], - lifetime: [null, Validators.required], - defaultMinPeriod: [null, Validators.required], - notifIfDisabled: [true, []], - binding: [], - bootstrapServer: [null, Validators.required], - lwm2mServer: [null, Validators.required], - clientStrategy: [1, []], - fwUpdateStrategy: [1, []], - swUpdateStrategy: [1, []], - fwUpdateRecourse: [{value: '', disabled: true}, []], - swUpdateRecourse: [{value: '', disabled: true}, []] + bootstrap: this.fb.group({ + servers: this.fb.group({ + binding: [DEFAULT_BINDING], + shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(0)]], + lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0)]], + notifIfDisabled: [DEFAULT_NOTIF_IF_DESIBLED, []], + defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0)]], + }), + bootstrapServer: [null, Validators.required], + lwm2mServer: [null, Validators.required] + }), + clientLwM2mSettings: this.fb.group({ + clientStrategy: [1, []], + fwUpdateStrategy: [1, []], + swUpdateStrategy: [1, []], + fwUpdateRecourse: [{value: '', disabled: true}, []], + swUpdateRecourse: [{value: '', disabled: true}, []] + }) }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] }); - this.lwm2mDeviceProfileFormGroup.get('fwUpdateStrategy').valueChanges.pipe( + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((fwStrategy) => { if (fwStrategy === 2) { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse') + .patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); this.isFwUpdateStrategy = true; } else { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').disable({emitEvent: false}); this.isFwUpdateStrategy = false; } this.otaUpdateFwStrategyValidate(true); }); - this.lwm2mDeviceProfileFormGroup.get('swUpdateStrategy').valueChanges.pipe( + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((swStrategy) => { if (swStrategy === 2) { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse') + .patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); this.isSwUpdateStrategy = true; } else { this.isSwUpdateStrategy = false; - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').disable({emitEvent: false}); } this.otaUpdateSwStrategyValidate(true); }); @@ -209,18 +218,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), - shortId: this.configurationValue.bootstrap.servers.shortId, - lifetime: this.configurationValue.bootstrap.servers.lifetime, - defaultMinPeriod: this.configurationValue.bootstrap.servers.defaultMinPeriod, - notifIfDisabled: this.configurationValue.bootstrap.servers.notifIfDisabled, - binding: this.configurationValue.bootstrap.servers.binding, - bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, - lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, - clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, - fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, - swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, - fwUpdateRecourse: fwResource, - swUpdateRecourse: swResource + bootstrap: this.configurationValue.bootstrap, + clientLwM2mSettings: { + clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, + fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, + fwUpdateRecourse: fwResource, + swUpdateRecourse: swResource + } }, {emitEvent: false}); this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; @@ -249,21 +254,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateDeviceProfileValue(config): void { if (this.lwm2mDeviceProfileFormGroup.valid) { this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); - this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer; - this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer; - const bootstrapServers = this.configurationValue.bootstrap.servers; - bootstrapServers.shortId = config.shortId; - bootstrapServers.lifetime = config.lifetime; - bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; - bootstrapServers.notifIfDisabled = config.notifIfDisabled; - bootstrapServers.binding = config.binding; - this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; - this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; - this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; - this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse; - this.upDateJsonAllConfig(); } + this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer; + this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; + this.configurationValue.bootstrap.servers = config.bootstrap.servers; + this.configurationValue.clientLwM2mSettings = config.clientLwM2mSettings; + this.upDateJsonAllConfig(); this.updateModel(); } @@ -539,20 +535,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private otaUpdateFwStrategyValidate(updated = false): void { if (this.isFwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').setValidators([Validators.required]); } else { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity({emitEvent: updated}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateRecourse').updateValueAndValidity({emitEvent: updated}); } private otaUpdateSwStrategyValidate(updated = false): void { if (this.isSwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').setValidators([Validators.required]); } else { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity({emitEvent: updated}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateRecourse').updateValueAndValidity({emitEvent: updated}); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 1f2d125d9f..a5d3502808 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -47,7 +47,7 @@ export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_ export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ':' + DEFAULT_PORT_SERVER_NO_SEC; -export enum BINDING_MODE { +export enum BingingMode { U = 'U', UQ = 'UQ', T = 'T', @@ -60,18 +60,18 @@ export enum BINDING_MODE { TQS = 'TQS' } -export const BINDING_MODE_NAMES = new Map( +export const BingingModeTranslationsMap = new Map( [ - [BINDING_MODE.U, 'U: UDP connection in standard mode'], - [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], - [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], - [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'], - [BINDING_MODE.T, 'T: TCP connection in standard mode'], - [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], - [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], - [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'], - [BINDING_MODE.S, 'S: SMS connection in standard mode'], - [BINDING_MODE.SQ, 'SQ: SMS connection in queue mode'] + [BingingMode.U, 'device-profile.lwm2m.binding-type.u'], + [BingingMode.UQ, 'device-profile.lwm2m.binding-type.uq'], + [BingingMode.US, 'device-profile.lwm2m.binding-type.us'], + [BingingMode.UQS, 'device-profile.lwm2m.binding-type.uqs'], + [BingingMode.T, 'device-profile.lwm2m.binding-type.t'], + [BingingMode.TQ, 'device-profile.lwm2m.binding-type.tq'], + [BingingMode.TS, 'device-profile.lwm2m.binding-type.ts'], + [BingingMode.TQS, 'device-profile.lwm2m.binding-type.tqs'], + [BingingMode.S, 'device-profile.lwm2m.binding-type.s'], + [BingingMode.SQ, 'device-profile.lwm2m.binding-type.sq'] ] ); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ec084b8a3a..ddc3e83061 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1257,10 +1257,24 @@ "servers": "Servers", "short-id": "Short ID", "short-id-required": "Short ID is required.", - "lifetime": "Lifetime of the registration for this LwM2M client", + "lifetime": "LwM2M Client registration Lifetime", + "lifetime-required": "LwM2M Client registration Lifetime is required.", "default-min-period": "Minimum Period between two notifications (sec)", - "notif-if-disabled": "Notification Storing When Disabled or Offline", + "default-min-period-required": "Minimum Period is required.", + "notification-storing": "Notification storing when disabled or offline", "binding": "Binding", + "binding-type": { + "u": "U: UDP connection in standard mode", + "uq": "UQ: UDP connection in queue mode", + "us": "US: both UDP and SMS connections active, both in standard mode", + "uqs": "UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode", + "t": "T: TCP connection in standard mode", + "tq": "TQ: TCP connection in queue mode", + "ts": "TS: both TCP and SMS connections active, both in standard mode", + "tqs": "TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode", + "s": "S: SMS connection in standard mode", + "sq": "SQ: SMS connection in queue mode" + }, "bootstrap-tab": "Bootstrap", "bootstrap-server": "Bootstrap Server", "lwm2m-server": "LwM2M Server", From 51e7549b58428bef1540d80b715c687a66c4ec37 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 15 Jun 2021 16:22:23 +0300 Subject: [PATCH 044/151] Await execution for sendReadRequests, sendObserveRequests --- .../server/downlink/TbLwM2MLatchCallback.java | 45 +++++++++++++++++++ .../server/downlink/TbLwM2MReadCallback.java | 4 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 41 ++++++++++++++--- 3 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MLatchCallback.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MLatchCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MLatchCallback.java new file mode 100644 index 0000000000..a864275487 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MLatchCallback.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.downlink; + +import lombok.RequiredArgsConstructor; + +import java.util.concurrent.CountDownLatch; + +@RequiredArgsConstructor +public class TbLwM2MLatchCallback implements DownlinkRequestCallback { + + private final CountDownLatch countDownLatch; + private final DownlinkRequestCallback callback; + + @Override + public void onSuccess(R request, T response) { + callback.onSuccess(request, response); + countDownLatch.countDown(); + } + + @Override + public void onValidationError(String params, String msg) { + callback.onValidationError(params, msg); + countDownLatch.countDown(); + } + + @Override + public void onError(String params, Exception e) { + callback.onError(params, e); + countDownLatch.countDown(); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java index b6d0801f40..46315a6fcb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java @@ -18,10 +18,8 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.response.ReadResponse; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; - -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @Slf4j public class TbLwM2MReadCallback extends TbLwM2MTargetedCallback { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 15df144cc4..b1ffe66600 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -28,7 +28,10 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.ObserveRequest; +import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.WriteRequest; +import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; import org.springframework.context.annotation.Lazy; @@ -69,7 +72,9 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; @@ -99,6 +104,7 @@ import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -510,16 +516,29 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Set targetIds = new HashSet<>(profile.getObserveAttr().getAttribute()); targetIds.addAll(profile.getObserveAttr().getTelemetry()); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); - lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId)); + + CountDownLatch latch = new CountDownLatch(targetIds.size()); + targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId, + new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)))); + try { + latch.await(); + } catch (InterruptedException e) { + log.error("Failed to await Read requests!"); + } } private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { Set targetIds = profile.getObserveAttr().getObserve(); targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); -// TODO: why do we need to put observe into pending read requests? -// lwM2MClient.getPendingReadRequests().addAll(targetIds); - targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId)); + + CountDownLatch latch = new CountDownLatch(targetIds.size()); + targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, + new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, lwM2MClient, targetId)))); + try { + latch.await(); + } catch (InterruptedException e) { + log.error("Failed to await Observe requests!"); + } } private void sendWriteAttributeRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set supportedObjects) { @@ -544,13 +563,21 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { + sendReadRequest(lwM2MClient, versionedId, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)); + } + + private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, callback); } private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { + sendObserveRequest(lwM2MClient, versionedId, new TbLwM2MObserveCallback(this, lwM2MClient, versionedId)); + } + + private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, new TbLwM2MObserveCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, callback); } private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { From dc7c96c4f03f5e7da6c05b0d13cf1c17e95a6160 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 15 Jun 2021 17:35:40 +0300 Subject: [PATCH 045/151] Refactoring to extract AttributeService --- .../secure/LwM2MBootstrapSecurityStore.java | 2 +- .../lwm2m/server/LwM2mServerListener.java | 3 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 6 +- .../DefaultLwM2MAttributesService.java | 150 +++++++++ .../attributes/LwM2MAttributesService.java | 32 ++ .../lwm2m/server/client/LwM2mClient.java | 6 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 8 +- .../DefaultLwM2mDownlinkMsgHandler.java | 196 +----------- .../downlink/TbLwM2MObserveCallback.java | 2 +- .../server/downlink/TbLwM2MReadCallback.java | 2 +- .../ota/DefaultLwM2MOtaUpdateService.java | 83 +++++ .../lwm2m/server/ota/LwM2MClientOtaState.java | 19 ++ .../server/ota/LwM2MOtaUpdateService.java | 24 ++ .../rpc/DefaultLwM2MRpcRequestHandler.java | 37 --- .../server/rpc/LwM2mClientRpcRequest.java | 284 ------------------ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 210 ++++--------- .../server/uplink/LwM2mUplinkMsgHandler.java | 9 +- 17 files changed, 382 insertions(+), 691 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 310d738757..80aa42d6af 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -155,7 +155,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); UUID sessionUUiD = UUID.randomUUID(); TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, sessionInfo)); + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo)); if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 0d566974d2..36f1e8a903 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -93,8 +93,7 @@ public class LwM2mServerListener { @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (registration != null) { - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), - registration), response, null); + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index 7448f9c71d..ebf0a48aba 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -31,6 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; +import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @@ -42,17 +43,18 @@ import java.util.UUID; @RequiredArgsConstructor public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener { private final LwM2mUplinkMsgHandler handler; + private final LwM2MAttributesService attributesService; private final LwM2MRpcRequestHandler rpcHandler; private final TransportProtos.SessionInfoProto sessionInfo; @Override public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { - this.handler.onGetAttributesResponse(getAttributesResponse, this.sessionInfo); + this.attributesService.onGetAttributesResponse(getAttributesResponse, this.sessionInfo); } @Override public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) { - this.handler.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo); + this.attributesService.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java new file mode 100644 index 0000000000..9abe30dbaf --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -0,0 +1,150 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.attributes; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.model.ResourceModel; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords; + +@Slf4j +@Service +@TbLwM2mTransportComponent +@RequiredArgsConstructor +public class DefaultLwM2MAttributesService implements LwM2MAttributesService { + + //TODO: add timeout logic + private final AtomicInteger reqIdSeq = new AtomicInteger(); + private final Map>> futures; + + private final TransportService transportService; + + @Override + public ListenableFuture> getSharedAttributes(LwM2mClient client, Collection keys) { + SettableFuture> future = SettableFuture.create(); + int requestId = reqIdSeq.incrementAndGet(); + futures.put(requestId, future); + transportService.process(client.getSession(), TransportProtos.GetAttributeRequestMsg.newBuilder().setRequestId(requestId). + addAllSharedAttributeNames(keys).build(), new TransportServiceCallback() { + @Override + public void onSuccess(Void msg) { + + } + + @Override + public void onError(Throwable e) { + SettableFuture> callback = futures.remove(requestId); + if (callback != null) { + callback.setException(e); + } + } + }); + return future; + } + + @Override + public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo) { + var callback = futures.remove(getAttributesResponse.getRequestId()); + if (callback != null) { + callback.set(getAttributesResponse.getSharedAttributeListList()); + } + } + + /** + * Update - send request in change value resources in Client + * 1. FirmwareUpdate: + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 + * 2. Shared Other AttributeUpdate + * -- Path to resources from profile equal keyName or from ModelObject equal name + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) + * 3. Delete - nothing + * + * @param msg - + */ + @Override + public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { +// LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); +// if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { +// log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); +// msg.getSharedUpdatedList().forEach(tsKvProto -> { +// String pathName = tsKvProto.getKv().getKey(); +// String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, pathName); +// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); +// if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) +// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) +// || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) +// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { +// this.getInfoFirmwareUpdate(lwM2MClient, null); +// } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) +// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) +// || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) +// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { +// this.getInfoSoftwareUpdate(lwM2MClient, null); +// } +// if (pathIdVer != null) { +// ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config +// .getModelProvider()); +// if (resourceModel != null && resourceModel.operations.isWritable()) { +// this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); +// } else { +// log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); +// String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", +// LOG_LWM2M_ERROR, pathIdVer, valueNew); +// this.logToTelemetry(lwM2MClient, logMsg); +// } +// } else if (!isFwSwWords(pathName)) { +// log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); +// String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", +// LOG_LWM2M_ERROR, pathName, valueNew); +// this.logToTelemetry(lwM2MClient, logMsg); +// } +// +// }); +// } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { +// msg.getSharedUpdatedList().forEach(tsKvProto -> { +// String pathName = tsKvProto.getKv().getKey(); +// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); +// if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { +// lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); +// } +// }); +// log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); +// } else if (lwM2MClient == null) { +// log.error("OnAttributeUpdate, lwM2MClient is null"); +// } + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java new file mode 100644 index 0000000000..008788cb33 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.attributes; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +import java.util.Collection; +import java.util.List; + +public interface LwM2MAttributesService { + + ListenableFuture> getSharedAttributes(LwM2mClient client, Collection keys); + + void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo); + + void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 80e85036c3..063b06263a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -75,7 +75,7 @@ public class LwM2mClient implements Cloneable { @Getter private final Map resources; @Getter - private final Map delayedRequests; + private final Map sharedAttributes; @Getter private final List pendingReadRequests; @Getter @@ -121,7 +121,7 @@ public class LwM2mClient implements Cloneable { this.nodeId = nodeId; this.endpoint = endpoint; this.lock = new ReentrantLock(); - this.delayedRequests = new ConcurrentHashMap<>(); + this.sharedAttributes = new ConcurrentHashMap<>(); this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.resources = new ConcurrentHashMap<>(); this.queuedRequests = new ConcurrentLinkedQueue<>(); @@ -371,7 +371,7 @@ public class LwM2mClient implements Cloneable { } if (this.pendingReadRequests.size() == 0) { this.init = true; - serviceImpl.putDelayedUpdateResourcesThingsboard(this); + serviceImpl.initAttributes(this); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 000e97ccc1..b19fa4ac55 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; -import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -127,9 +126,6 @@ public class LwM2mFwSwUpdate { private final List pendingInfoRequestsStart; @Getter @Setter - private volatile LwM2mClientRpcRequest rpcRequest; - @Getter - @Setter private volatile int updateStrategy; public LwM2mFwSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { @@ -214,10 +210,10 @@ public class LwM2mFwSwUpdate { } else { String msgError = "FirmWareId is null."; log.warn("6) [{}]", msgError); - if (this.rpcRequest != null) { +// if (this.rpcRequest != null) { // TODO: refactor. // handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); - } +// } log.error(msgError); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_ERROR, msgError); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 746b985812..da513a1934 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.observation.Observation; +import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DiscoverRequest; @@ -49,13 +50,10 @@ import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; -import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -289,200 +287,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { default: throw new IllegalArgumentException("Not supported type:" + type.name()); } - -// TODO: throw exception and execute callback. -//// if (rpcRequest != null) { -//// String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; -//// String errorMsg = String.format("Bad ResourceModel Operations (E): Resource path - %s ResourceModel type - %s", patn, type); -//// rpcRequest.setErrorMsg(errorMsg); -//// } -// return null; -// } catch (NumberFormatException e) { -// String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; -// String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", -// patn, type, value, e.toString()); -// handler.sendLogsToThingsboard(client, msg); -// log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); -// if (rpcRequest != null) { -// String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); -// handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); -// } -// return null; -// } - } - - private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response, - SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { - responseRequestExecutor.submit(() -> { - try { - this.sendResponse(lwM2mClient, path, response, request, rpcRequest); - } catch (Exception e) { - log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", lwM2mClient.getRegistration().getEndpoint(), path, e); - } - }); } - /** - * processing a response from a client - * - * @param path - - * @param response - - */ - private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response, - SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { -// Registration registration = lwM2mClient.getRegistration(); -// String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); -// String msgLog = ""; -// if (response instanceof ReadResponse) { -// handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); -// } else if (response instanceof DeleteResponse) { -// log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response); -// if (rpcRequest != null) { -// rpcRequest.setInfoMsg(null); -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null); -// } -// } else if (response instanceof DiscoverResponse) { -// String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); -// msgLog = String.format("%s: type operation: %s path: %s value: %s", -// LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); -// handler.sendLogsToThingsboard(lwM2mClient, msgLog); -// log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); -// if (rpcRequest != null) { -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); -// } -// } else if (response instanceof ExecuteResponse) { -// msgLog = String.format("%s: type operation: %s path: %s", -// LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); -// log.warn("9) [{}] ", msgLog); -// handler.sendLogsToThingsboard(lwM2mClient, msgLog); -// if (rpcRequest != null) { -// msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); -// rpcRequest.setInfoMsg(msgLog); -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO); -// } -// -// } else if (response instanceof WriteAttributesResponse) { -// msgLog = String.format("%s: type operation: %s path: %s value: %s", -// LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); -// handler.sendLogsToThingsboard(lwM2mClient, msgLog); -// log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); -// if (rpcRequest != null) { -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); -// } -// } else if (response instanceof WriteResponse) { -// msgLog = String.format("Type operation: Write path: %s", pathIdVer); -// log.warn("10) [{}] response: [{}]", msgLog, response); -// this.infoWriteResponse(lwM2mClient, response, request, rpcRequest); -// handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); -// } - } - -// private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest -// request, LwM2mClientRpcRequest rpcRequest) { -// try { -// Registration registration = lwM2mClient.getRegistration(); -// LwM2mNode node = ((WriteRequest) request).getNode(); -// String msg = null; -// Object value; -// if (node instanceof LwM2mObject) { -// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", -// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString()); -// } else if (node instanceof LwM2mObjectInstance) { -// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", -// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint()); -// } else if (node instanceof LwM2mSingleResource) { -// LwM2mSingleResource singleResource = (LwM2mSingleResource) node; -// if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { -// int valueLength; -// if (singleResource.getType() == ResourceModel.Type.STRING) { -// valueLength = ((String) singleResource.getValue()).length(); -// value = ((String) singleResource.getValue()) -// .substring(Math.min(valueLength, config.getLogMaxLength())).trim(); -// -// } else { -// valueLength = ((byte[]) singleResource.getValue()).length; -// value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), -// Math.min(valueLength, config.getLogMaxLength()))).trim(); -// } -// value = valueLength > config.getLogMaxLength() ? value + "..." : value; -// msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", -// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); -// } else { -// value = this.converter.convertValue(singleResource.getValue(), -// singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); -// msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", -// LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); -// } -// } -// if (msg != null) { -// handler.sendLogsToThingsboard(lwM2mClient, msg); -// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { -// this.afterWriteSuccessFwSwUpdate(registration, request); -// if (rpcRequest != null) { -// rpcRequest.setInfoMsg(msg); -// } -// } else if (rpcRequest != null) { -// handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); -// } -// } -// } catch (Exception e) { -// log.trace("Fail convert value from request to string. ", e); -// } -// } - - /** - * After finish operation FwSwUpdate Write (success): - * fw_state/sw_state = DOWNLOADED - * send operation Execute - */ -// private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) { -// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); -// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { -// client.getFwUpdate().setStateUpdate(DOWNLOADED.name()); -// client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); -// } -// if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { -// client.getSwUpdate().setStateUpdate(DOWNLOADED.name()); -// client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); -// } -// } - - /** - * After finish operation FwSwUpdate Write (error): fw_state = FAILED - */ -// private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String -// msgError) { -// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); -// if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && client.getFwUpdate() != null) { -// client.getFwUpdate().setStateUpdate(FAILED.name()); -// client.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); -// } -// if (request.getPath().toString().equals(SW_PACKAGE_ID) && client.getSwUpdate() != null) { -// client.getSwUpdate().setStateUpdate(FAILED.name()); -// client.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); -// } -// } - -// private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String -// msgError) { -// LwM2mClient client = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); -// if (request.getPath().toString().equals(FW_UPDATE_ID) && client.getFwUpdate() != null) { -// client.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); -// } -// if (request.getPath().toString().equals(SW_INSTALL_ID) && client.getSwUpdate() != null) { -// client.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); -// } -// } - -// private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String -// observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { -// handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg); -// log.warn("[{}]", observeCancelMsg); -// if (rpcRequest != null) { -// rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); -// handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); -// } -// } private void validateVersionedId(LwM2mClient client, HasVersionedId request) { if (!client.isValidObjectVersion(request.getVersionedId())) { throw new IllegalArgumentException("Specified resource id is not configured in the device profile!"); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java index e5453902fb..7fca29aa8c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java @@ -31,6 +31,6 @@ public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback fwStates = new ConcurrentHashMap<>(); + private final Map swStates = new ConcurrentHashMap<>(); + + private final LwM2MAttributesService attributesService; + private final TransportService transportService; + private final LwM2mClientContext clientContext; + + @Override + public void init(LwM2mClient client) { + //TODO: check that the client supports FW and SW by checking the supported objects in the model. + List attributesToFetch = new ArrayList<>(); + if (client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)) { + LwM2MClientOtaState fwState = getOrInitFwSate(client); + attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE)); + attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION)); + } + + if (client.isValidObjectVersion(SW_NAME_ID) || client.isValidObjectVersion(SW_VER_ID)) { + LwM2MClientOtaState swState = getOrInitSwSate(client); + attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE)); + attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION)); + } + + var future = attributesService.getSharedAttributes(client, attributesToFetch); + } + + private LwM2MClientOtaState getOrInitFwSate(LwM2mClient client) { + //TODO: fetch state from the cache. + return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + } + + private LwM2MClientOtaState getOrInitSwSate(LwM2mClient client) { + //TODO: fetch state from the cache. + return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java new file mode 100644 index 0000000000..43dd874a09 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota; + +public class LwM2MClientOtaState { +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java new file mode 100644 index 0000000000..9dcbab5d9e --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota; + +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public interface LwM2MOtaUpdateService { + + void init(LwM2mClient client); + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 80dad717aa..b2c8f73ac8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -58,11 +58,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_VALUE; - @Slf4j @Service @TbLwM2mTransportComponent @@ -258,17 +253,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { transportService.process(sessionInfo, msg, null); } - private void sendErrorRpcResponse(LwM2mClientRpcRequest lwm2mClientRpcRequest, String msgError, TransportProtos.SessionInfoProto sessionInfo) { - if (lwm2mClientRpcRequest == null) { - lwm2mClientRpcRequest = new LwM2mClientRpcRequest(); - } - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); - if (lwm2mClientRpcRequest.getErrorMsg() == null) { - lwm2mClientRpcRequest.setErrorMsg(msgError); - } - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); - } - private void cleanupOldSessions() { log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); if (rpcSubscriptions.size() > 0) { @@ -281,27 +265,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); } - public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { - rpcRequest.setResponseCode(requestCode); - if (LOG_LWM2M_ERROR.equals(typeMsg)) { - rpcRequest.setInfoMsg(null); - rpcRequest.setValueMsg(null); - if (rpcRequest.getErrorMsg() == null) { - msg = msg.isEmpty() ? null : msg; - rpcRequest.setErrorMsg(msg); - } - } else if (LOG_LWM2M_INFO.equals(typeMsg)) { - if (rpcRequest.getInfoMsg() == null) { - rpcRequest.setInfoMsg(msg); - } - } else if (LOG_LWM2M_VALUE.equals(typeMsg)) { - if (rpcRequest.getValueMsg() == null) { - rpcRequest.setValueMsg(msg); - } - } - this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); - } - @Override public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, TransportProtos.SessionInfoProto sessionInfo) { log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java deleted file mode 100644 index 1ec91015b2..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2mClientRpcRequest.java +++ /dev/null @@ -1,284 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.server.rpc; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.leshan.core.node.LwM2mPath; -import org.eclipse.leshan.server.registration.Registration; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeoutException; - -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.ERROR_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_JSON_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY; - -import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; - -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.DISCOVER_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.FW_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_READ_ALL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_ATTRIBUTES; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SEPARATOR_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer; - -@Slf4j -@Data -public class LwM2mClientRpcRequest { - - private Registration registration; - private TransportProtos.SessionInfoProto sessionInfo; - private String bodyParams; - private int requestId; - - private LwM2mOperationType typeOper; - private String key; - private String targetIdVer; - private Object value; - private Map params; - - private String errorMsg; - private String valueMsg; - private String infoMsg; - private String responseCode; - - public LwM2mClientRpcRequest() { - } - - public LwM2mClientRpcRequest(LwM2mOperationType lwM2mTypeOper, String bodyParams, int requestId, - TransportProtos.SessionInfoProto sessionInfo, Registration registration, LwM2mUplinkMsgHandler handler) { - this.registration = registration; - this.sessionInfo = sessionInfo; - this.requestId = requestId; - if (lwM2mTypeOper != null) { - this.typeOper = lwM2mTypeOper; - } else { - this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid."; - } - if (this.errorMsg == null && !bodyParams.equals("null")) { - this.bodyParams = bodyParams; - this.init(handler); - } - } - - public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() { - JsonObject payloadResp = new JsonObject(); - payloadResp.addProperty(RESULT_KEY, this.responseCode); - if (this.errorMsg != null) { - payloadResp.addProperty(ERROR_KEY, this.errorMsg); - } else if (this.valueMsg != null) { - payloadResp.addProperty(VALUE_KEY, this.valueMsg); - } else if (this.infoMsg != null) { - payloadResp.addProperty(INFO_KEY, this.infoMsg); - } - return TransportProtos.ToDeviceRpcResponseMsg.newBuilder() - .setPayload(payloadResp.getAsJsonObject().toString()) - .setRequestId(this.requestId) - .build(); - } - - private void init(LwM2mUplinkMsgHandler handler) { - try { - // #1 - if (this.bodyParams.contains(KEY_NAME_KEY)) { - String targetIdVerStr = this.getValueKeyFromBody(KEY_NAME_KEY); - if (targetIdVerStr != null) { - String targetIdVer = handler.getObjectIdByKeyNameFromProfile(sessionInfo, targetIdVerStr); - if (targetIdVer != null) { - this.targetIdVer = targetIdVer; - this.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s", - targetIdVerStr, targetIdVer)); - } - } - } - if (this.getTargetIdVer() == null && this.bodyParams.contains(TARGET_ID_VER_KEY)) { - this.setValidTargetIdVerKey(); - } - if (this.bodyParams.contains(VALUE_KEY)) { - this.value = this.getValueKeyFromBody(VALUE_KEY); - } - try { - if (this.bodyParams.contains(PARAMS_KEY)) { - this.setValidParamsKey(handler); - } - } catch (Exception e) { - this.setErrorMsg(String.format("Params of request is bad Json format. %s", e.getMessage())); - } - - if (this.getTargetIdVer() == null - && !(OBSERVE_READ_ALL == this.getTypeOper() - || DISCOVER_ALL == this.getTypeOper() - || OBSERVE_CANCEL == this.getTypeOper() - || FW_UPDATE == this.getTypeOper())) { - this.setErrorMsg(TARGET_ID_VER_KEY + " and " + - KEY_NAME_KEY + " is null or bad format"); - } - /** - * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance - */ - else if (this.getTargetIdVer() != null - && (EXECUTE == this.getTypeOper() - || WRITE_REPLACE == this.getTypeOper()) - && !(new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.getTargetIdVer()))).isResource() - || new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.getTargetIdVer()))).isResourceInstance())) { - this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY - + ". Only Resource or ResourceInstance can be this operation"); - } - } catch (Exception e) { - this.setErrorMsg(String.format("Bad format request. %s", e.getMessage())); - } - - } - - private void setValidTargetIdVerKey() { - String targetIdVerStr = this.getValueKeyFromBody(TARGET_ID_VER_KEY); - // targetIdVer without ver - ok - try { - // targetIdVer with/without ver - ok - this.targetIdVer = validPathIdVer(targetIdVerStr, this.registration); - if (this.targetIdVer != null) { - this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer); - } - } catch (Exception e) { - if (this.targetIdVer == null) { - this.errorMsg = TARGET_ID_VER_KEY + " - " + targetIdVerStr + " is not valid."; - } - } - } - - private void setValidParamsKey(LwM2mUplinkMsgHandler handler) { - String paramsStr = this.getValueKeyFromBody(PARAMS_KEY); - if (paramsStr != null) { - String params2Json = - START_JSON_KEY - + "\"" - + paramsStr - .replaceAll(SEPARATOR_KEY, "\"" + SEPARATOR_KEY + "\"") - .replaceAll(FINISH_VALUE_KEY, "\"" + FINISH_VALUE_KEY + "\"") - + "\"" - + FINISH_JSON_KEY; - // jsonObject - Map params = new Gson().fromJson(params2Json, new TypeToken>() { - }.getType()); - if (WRITE_UPDATE == this.getTypeOper()) { - if (this.targetIdVer != null) { - Map paramsResourceId = this.convertParamsToResourceId((ConcurrentHashMap) params, handler); - if (paramsResourceId.size() > 0) { - this.setParams(paramsResourceId); - } - } - } else if (WRITE_ATTRIBUTES == this.getTypeOper()) { - this.setParams(params); - } - } - } - - private String getValueKeyFromBody(String key) { - String valueKey = null; - int startInd = -1; - int finishInd = -1; - try { - switch (key) { - case KEY_NAME_KEY: - case TARGET_ID_VER_KEY: - case VALUE_KEY: - startInd = this.bodyParams.indexOf(SEPARATOR_KEY, this.bodyParams.indexOf(key)); - finishInd = this.bodyParams.indexOf(FINISH_VALUE_KEY, this.bodyParams.indexOf(key)); - if (startInd >= 0 && finishInd < 0) { - finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); - } - break; - case PARAMS_KEY: - startInd = this.bodyParams.indexOf(START_JSON_KEY, this.bodyParams.indexOf(key)); - finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); - } - if (startInd >= 0 && finishInd > 0) { - valueKey = this.bodyParams.substring(startInd + 1, finishInd); - } - } catch (Exception e) { - log.error("", new TimeoutException()); - } - /** - * ReplaceAll "\"" - */ - if (StringUtils.trimToNull(valueKey) != null) { - char[] chars = valueKey.toCharArray(); - for (int i = 0; i < chars.length; i++) { - if (chars[i] == 92 || chars[i] == 34) chars[i] = 32; - } - return key.equals(PARAMS_KEY) ? String.valueOf(chars) : String.valueOf(chars).replaceAll(" ", ""); - } - return null; - } - - private ConcurrentHashMap convertParamsToResourceId(ConcurrentHashMap params, - LwM2mUplinkMsgHandler serviceImpl) { - Map paramsIdVer = new ConcurrentHashMap<>(); - LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(this.targetIdVer))); - if (targetId.isObjectInstance()) { - params.forEach((k, v) -> { - try { - int id = Integer.parseInt(k); - paramsIdVer.put(String.valueOf(id), v); - } catch (NumberFormatException e) { - String targetIdVer = serviceImpl.getObjectIdByKeyNameFromProfile(sessionInfo, k); - if (targetIdVer != null) { - LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(fromVersionedIdToObjectId(targetIdVer))); - paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); - } - /** WRITE_UPDATE*/ - else { - String rezId = this.getRezIdByResourceNameAndObjectInstanceId(k, serviceImpl); - if (rezId != null) { - paramsIdVer.put(rezId, v); - } - } - } - }); - } - return (ConcurrentHashMap) paramsIdVer; - } - - private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, LwM2mUplinkMsgHandler handler) { -// LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo); -// return lwM2mClient != null ? -// lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : -// null; - return null; - } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 34163815d7..57d6c1b071 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -33,6 +33,7 @@ import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.server.registration.Registration; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; @@ -41,15 +42,12 @@ import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; @@ -61,6 +59,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; +import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -83,7 +82,6 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; -import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -107,7 +105,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; @@ -119,8 +116,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @@ -136,6 +131,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final TransportService transportService; private final LwM2mTransportContext context; + private final LwM2MAttributesService attributesService; public final LwM2MTransportServerConfig config; public final OtaPackageDataCache otaPackageDataCache; public final LwM2mTransportServerHelper helper; @@ -147,13 +143,14 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public final Map firmwareUpdateState; - public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MAttributesService attributesService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; + this.attributesService = attributesService; this.config = config; this.helper = helper; this.clientContext = clientContext; @@ -167,7 +164,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } @PostConstruct - public void init() { + public void initAttributes() { this.context.getScheduler().scheduleAtFixedRate(this::reportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS); this.registrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getRegisteredPoolSize(), "LwM2M registration"); this.updateRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUpdateRegisteredPoolSize(), "LwM2M update registration"); @@ -202,7 +199,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } this.logToTelemetry(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); SessionInfoProto sessionInfo = lwM2MClient.getSession(); - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) @@ -211,9 +208,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) .build(); transportService.process(msg, null); - this.getInfoFirmwareUpdate(lwM2MClient, null); - this.getInfoSoftwareUpdate(lwM2MClient, null); + this.getInfoFirmwareUpdate(lwM2MClient); + this.getInfoSoftwareUpdate(lwM2MClient); this.initClientTelemetry(lwM2MClient); + this.initAttributes(lwM2MClient); } else { log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); } @@ -324,7 +322,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param response - observe */ @Override - public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest) { + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response) { if (response.getContent() != null) { LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); @@ -343,70 +341,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - /** - * Update - send request in change value resources in Client - * 1. FirmwareUpdate: - * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 - * 2. Shared Other AttributeUpdate - * -- Path to resources from profile equal keyName or from ModelObject equal name - * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) - * 3. Delete - nothing - * - * @param msg - - */ - @Override - public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); - if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { - log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); - msg.getSharedUpdatedList().forEach(tsKvProto -> { - String pathName = tsKvProto.getKv().getKey(); - String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, pathName); - Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) - && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) - || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) - && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { - this.getInfoFirmwareUpdate(lwM2MClient, null); - } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) - && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) - || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) - && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { - this.getInfoSoftwareUpdate(lwM2MClient, null); - } - if (pathIdVer != null) { - ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config - .getModelProvider()); - if (resourceModel != null && resourceModel.operations.isWritable()) { - this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); - } else { - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); - String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", - LOG_LWM2M_ERROR, pathIdVer, valueNew); - this.logToTelemetry(lwM2MClient, logMsg); - } - } else if (!isFwSwWords(pathName)) { - log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); - String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", - LOG_LWM2M_ERROR, pathName, valueNew); - this.logToTelemetry(lwM2MClient, logMsg); - } - - }); - } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { - msg.getSharedUpdatedList().forEach(tsKvProto -> { - String pathName = tsKvProto.getKv().getKey(); - Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { - lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); - } - }); - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); - } else if (lwM2MClient == null) { - log.error("OnAttributeUpdate, lwM2MClient is null"); - } - } - /** * @param sessionInfo - * @param deviceProfile - @@ -929,7 +863,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { + private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(this, lwM2MClient, versionedId)); @@ -973,25 +907,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { ).getKey(); } - /** - * 1. FirmwareUpdate: - * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 - * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values - * - Get path resource by result attributesResponse - * - * @param attributesResponse - - * @param sessionInfo - - */ - @Override - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { - try { - List tsKvProtos = attributesResponse.getSharedAttributeListList(); - this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo); - } catch (Exception e) { - log.error("", e); - } - } - /** * #1.1 If two names have equal path => last time attribute * #2.1 if there is a difference in values between the current resource values and the shared attribute values @@ -1000,31 +915,27 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * #2.1 if there is not a difference in values between the current resource values and the shared attribute values * * @param tsKvProtos - * @param sessionInfo */ - public void updateAttributeFromThingsboard(List tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); - if (lwM2MClient != null) { - log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); - tsKvProtos.forEach(tsKvProto -> { - String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, tsKvProto.getKv().getKey()); - if (pathIdVer != null) { - // #1.1 - if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) { - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); - } else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) { - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); + public void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos) { + log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); + tsKvProtos.forEach(tsKvProto -> { + String pathIdVer = this.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); + if (pathIdVer != null) { + // #1.1 + if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { + if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); } + } else { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); } - }); - // #2.1 - lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { - this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), - getValueFromKvProto(tsKvProto.getKv()), pathIdVer); - }); - } else { - log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); - } + } + }); + // #2.1 + lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { + this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), + getValueFromKvProto(tsKvProto.getKv()), pathIdVer); + }); } /** @@ -1053,7 +964,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { */ private void reportActivityAndRegister(SessionInfoProto sessionInfo) { if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, rpcHandler, sessionInfo)); + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); this.reportActivitySubscription(sessionInfo); } } @@ -1072,25 +983,20 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * * @param lwM2MClient - LwM2M Client */ - public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); - if (sessionInfo != null) { - //#1.1 - Map keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); - if (keyNamesMap.values().size() > 0) { - try { - //#1.2 - TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(null, keyNamesMap.values()); - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); - } catch (AdaptorException e) { - log.trace("Failed to decode get attributes request", e); - } - } - + public void initAttributes(LwM2mClient lwM2MClient) { + Map keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); + if (!keyNamesMap.isEmpty()) { + Set keysToFetch = new HashSet<>(keyNamesMap.values()); + keysToFetch.removeAll(OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS); + keysToFetch.removeAll(OtaPackageUtil.ALL_SW_ATTRIBUTE_KEYS); + DonAsynchron.withCallback(attributesService.getSharedAttributes(lwM2MClient, keysToFetch), + v -> onAttributesUpdate(lwM2MClient, v), + t -> log.error("[{}] Failed to get attributes", lwM2MClient.getEndpoint(), t), + registrationExecutor); } } - public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) { if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); if (sessionInfo != null) { @@ -1102,20 +1008,20 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(DefaultLwM2MUplinkMsgHandler.this, clientContext); - if (rpcRequest != null) { - fwUpdate.setStateUpdate(INITIATED.name()); - } +// if (rpcRequest != null) { +// fwUpdate.setStateUpdate(INITIATED.name()); +// } if (!FAILED.name().equals(fwUpdate.getStateUpdate())) { log.warn("7) firmware start with ver: [{}]", response.getVersion()); - fwUpdate.setRpcRequest(rpcRequest); +// fwUpdate.setRpcRequest(rpcRequest); fwUpdate.setCurrentVersion(response.getVersion()); fwUpdate.setCurrentTitle(response.getTitle()); fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); - if (rpcRequest == null) { +// if (rpcRequest == null) { fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); - } else { - fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); - } +// } else { +// fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); +// } } else { String msgError = String.format("OtaPackage device: %s, version: %s, stateUpdate: %s", lwM2MClient.getDeviceName(), response.getVersion(), fwUpdate.getStateUpdate()); @@ -1125,10 +1031,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { String msgError = String.format("OtaPackage device: %s, responseStatus: %s", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace(msgError); - if (rpcRequest != null) { +// if (rpcRequest != null) { //TODO: refactor // sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); - } +// } } } @@ -1141,7 +1047,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { + public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient) { if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); if (sessionInfo != null) { @@ -1152,16 +1058,16 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.SOFTWARE.name())) { - lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); +// lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); - if (rpcRequest == null) { +// if (rpcRequest == null) { lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); - } else { - lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); - } +// } else { +// lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); +// } } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index f29deb1cf3..041af97474 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; import java.util.Collection; import java.util.Optional; @@ -39,15 +38,13 @@ public interface LwM2mUplinkMsgHandler { void onSleepingDev(Registration registration); - void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest); - - void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response); void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile); void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt); - void onResourceUpdate (Optional resourceUpdateMsgOpt); + void onResourceUpdate(Optional resourceUpdateMsgOpt); void onResourceDelete(Optional resourceDeleteMsgOpt); @@ -67,7 +64,5 @@ public interface LwM2mUplinkMsgHandler { String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); - void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo); - LwM2MTransportServerConfig getConfig(); } From 6d7a8706097cc7c703d2d07e2231ded76f442703 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 15 Jun 2021 17:43:51 +0300 Subject: [PATCH 046/151] Correct sequence of client initialization --- .../lwm2m/server/client/LwM2mClient.java | 17 ----------------- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 18 +++++++++--------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 063b06263a..ec187549c8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -77,8 +77,6 @@ public class LwM2mClient implements Cloneable { @Getter private final Map sharedAttributes; @Getter - private final List pendingReadRequests; - @Getter private final Queue queuedRequests; @Getter @@ -110,9 +108,6 @@ public class LwM2mClient implements Cloneable { private ValidateDeviceCredentialsResponse credentials; - @Getter - private boolean init; - public Object clone() throws CloneNotSupportedException { return super.clone(); } @@ -122,7 +117,6 @@ public class LwM2mClient implements Cloneable { this.endpoint = endpoint; this.lock = new ReentrantLock(); this.sharedAttributes = new ConcurrentHashMap<>(); - this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.resources = new ConcurrentHashMap<>(); this.queuedRequests = new ConcurrentLinkedQueue<>(); this.state = LwM2MClientState.CREATED; @@ -132,7 +126,6 @@ public class LwM2mClient implements Cloneable { this.identity = identity; this.securityInfo = securityInfo; this.credentials = credentials; - this.init = false; this.session = createSession(nodeId, sessionId, credentials); this.tenantId = new TenantId(new UUID(session.getTenantIdMSB(), session.getTenantIdLSB())); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); @@ -365,16 +358,6 @@ public class LwM2mClient implements Cloneable { .collect(Collectors.toSet()); } - public void initReadValue(DefaultLwM2MUplinkMsgHandler serviceImpl, String path) { - if (path != null) { - this.pendingReadRequests.remove(path); - } - if (this.pendingReadRequests.size() == 0) { - this.init = true; - serviceImpl.initAttributes(this); - } - } - public ContentFormat getDefaultContentFormat() { if (registration == null) { return ContentFormat.DEFAULT; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 2bfc18d1f0..ec5fbbb6dd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -61,7 +61,6 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; @@ -86,6 +85,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -109,7 +109,6 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; @@ -138,10 +137,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final TransportService transportService; private final LwM2mTransportContext context; private final LwM2MAttributesService attributesService; + private final LwM2MOtaUpdateService otaService; public final LwM2MTransportServerConfig config; public final OtaPackageDataCache otaPackageDataCache; public final LwM2mTransportServerHelper helper; - private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; public final LwM2mClientContext clientContext; private final LwM2MRpcRequestHandler rpcHandler; @@ -149,14 +148,16 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public final Map firmwareUpdateState; - public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MAttributesService attributesService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MAttributesService attributesService, LwM2MOtaUpdateService otaService, + LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, - LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { + LwM2mTransportContext context, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.attributesService = attributesService; + this.otaService = otaService; this.config = config; this.helper = helper; this.clientContext = clientContext; @@ -164,7 +165,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; this.otaPackageDataCache = otaPackageDataCache; this.context = context; - this.adaptor = adaptor; this.firmwareUpdateState = new ConcurrentHashMap<>(); this.sessionStore = sessionStore; } @@ -218,6 +218,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.getInfoSoftwareUpdate(lwM2MClient); this.initClientTelemetry(lwM2MClient); this.initAttributes(lwM2MClient); + otaService.init(lwM2MClient); } else { log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); } @@ -441,7 +442,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (supportedObjects != null && supportedObjects.size() > 0) { if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { // #2 - lwM2MClient.getPendingReadRequests().addAll(supportedObjects); supportedObjects.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId)); } // #1 @@ -464,7 +464,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { try { latch.await(); } catch (InterruptedException e) { - log.error("Failed to await Read requests!"); + log.error("[{}] Failed to await Read requests!", lwM2MClient.getEndpoint()); } } @@ -478,7 +478,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { try { latch.await(); } catch (InterruptedException e) { - log.error("Failed to await Observe requests!"); + log.error("[{}] Failed to await Observe requests!", lwM2MClient.getEndpoint()); } } From 4e15160904952bfbda05d78fa022d6c07a8b2dd8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 16 Jun 2021 11:17:46 +0300 Subject: [PATCH 047/151] UI: Add dashboard logo sanitizer url; Added dashboard logo cache --- .../dashboard-page.component.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index cc19e98a99..5f3478bccc 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -128,6 +128,7 @@ import { DashboardImageDialogComponent, DashboardImageDialogData, DashboardImageDialogResult } from '@home/components/dashboard-page/dashboard-image-dialog.component'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; // @dynamic @Component({ @@ -211,7 +212,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC addingLayoutCtx: DashboardPageLayoutContext; - logo = 'assets/logo_title_white.svg'; + private dashboardLogoCache: SafeUrl; + private defaultDashboardLogo = 'assets/logo_title_white.svg'; dashboardCtx: DashboardContext = { instanceId: this.utils.guid(), @@ -312,7 +314,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC private ngZone: NgZone, private overlay: Overlay, private viewContainerRef: ViewContainerRef, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private sanitizer: DomSanitizer) { super(store); } @@ -413,6 +416,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC private reset() { this.dashboard = null; this.dashboardConfiguration = null; + this.dashboardLogoCache = undefined; this.prevDashboard = null; this.widgetEditMode = false; @@ -570,8 +574,12 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } } - public get dashboardLogo(): string { - return this.dashboard.configuration.settings.dashboardLogoUrl || this.logo; + public get dashboardLogo(): SafeUrl { + if (!this.dashboardLogoCache) { + const logo = this.dashboard.configuration.settings.dashboardLogoUrl || this.defaultDashboardLogo; + this.dashboardLogoCache = this.sanitizer.bypassSecurityTrustUrl(logo); + } + return this.dashboardLogoCache; } public showRightLayoutSwitch(): boolean { @@ -702,6 +710,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC }).afterClosed().subscribe((data) => { if (data) { this.dashboard.configuration.settings = data.settings; + this.dashboardLogoCache = undefined; const newGridSettings = data.gridSettings; if (newGridSettings) { const layout = this.dashboard.configuration.states[layoutKeys.state].layouts[layoutKeys.layout]; @@ -855,11 +864,13 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC if (this.widgetEditMode) { if (revert) { this.dashboard = this.prevDashboard; + this.dashboardLogoCache = undefined; } } else { this.resetHighlight(); if (revert) { this.dashboard = this.prevDashboard; + this.dashboardLogoCache = undefined; this.dashboardConfiguration = this.dashboard.configuration; this.dashboardCtx.dashboardTimewindow = this.dashboardConfiguration.timewindow; this.entityAliasesUpdated(); From b1204ebe5c2b110c276ab2d63eae3fd4b46b863b Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 16 Jun 2021 16:17:04 +0300 Subject: [PATCH 048/151] added PowerMode for LwM2M --- .../common/data/device/data/PowerMode.java | 20 ++++++ .../device/data/lwm2m/OtherConfiguration.java | 4 ++ .../server/DefaultLwM2mTransportService.java | 4 +- .../server/LwM2mTransportCoapResource.java | 8 +-- .../lwm2m/server/LwM2mTransportUtil.java | 4 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 4 +- ...ile-transport-configuration.component.html | 32 +++++++--- ...ofile-transport-configuration.component.ts | 62 ++++++++++--------- .../lwm2m/lwm2m-profile-config.models.ts | 28 +++++++-- .../assets/locale/locale.constant-en_US.json | 11 ++-- 10 files changed, 118 insertions(+), 59 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java new file mode 100644 index 0000000000..d7323e654a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/PowerMode.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.device.data; + +public enum PowerMode { + PSM, DRX, E_DRX +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java index 2bfef74391..0e0d09464a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -16,12 +16,16 @@ package org.thingsboard.server.common.data.device.data.lwm2m; import lombok.Data; +import org.thingsboard.server.common.data.device.data.PowerMode; @Data public class OtherConfiguration { private Integer fwUpdateStrategy; + private String fwUpdateResource; private Integer swUpdateStrategy; + private String swUpdateResource; private Integer clientOnlyObserveAfterConnect; + private PowerMode powerMode; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 97ab82f802..f5554f15d2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -64,7 +64,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE; @Slf4j @Component @@ -104,7 +104,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { */ - LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RECOURSE); + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RESOURCE); this.server.coap().getServer().add(otaCoapResource); this.startLhServer(); this.context.setServer(server); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index ff86ee9bd9..4f61685503 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -32,8 +32,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RESOURCE; @Slf4j public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { @@ -72,8 +72,8 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { protected void processHandleGet(CoapExchange exchange) { log.warn("90) processHandleGet [{}]", exchange); if (exchange.getRequestOptions().getUriPath().size() >= 2 && - (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) || - SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) { + (FIRMWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) || + SOFTWARE_UPDATE_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) { this.sendOtaData(exchange); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 0d8b96e74b..8ec2a9bd09 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -137,7 +137,7 @@ public class LwM2mTransportUtil { // Firmware - public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; + public static final String FIRMWARE_UPDATE_COAP_RESOURCE = "firmwareUpdateCoapResource"; public static final String FW_UPDATE = "Firmware update"; public static final Integer FW_5_ID = 5; public static final Integer FW_19_ID = 19; @@ -169,7 +169,7 @@ public class LwM2mTransportUtil { public static final String FW_UPDATE_ID = "/5/0/2"; // Software - public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; + public static final String SOFTWARE_UPDATE_COAP_RESOURCE = "softwareUpdateCoapResource"; public static final String SW_UPDATE = "Software update"; public static final Integer SW_ID = 9; // Package W diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index b19fa4ac55..f7ee2cc470 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -49,7 +49,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INIT import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RESOURCE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; @@ -199,7 +199,7 @@ public class LwM2mFwSwUpdate { request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { String apiFont = "coap://176.36.143.9:5685"; - String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); + String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RESOURCE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); //TODO: user this.rpcRequest??? TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 30b78393d3..75878c7078 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -126,10 +126,10 @@ - {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} - - - {{ 'device-profile.lwm2m.fw-update-recourse-required' | translate }} + {{ 'device-profile.lwm2m.fw-update-resource' | translate }} + + + {{ 'device-profile.lwm2m.fw-update-resource-required' | translate }}
@@ -143,13 +143,25 @@ - {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} - - - {{ 'device-profile.lwm2m.sw-update-recourse-required' | translate }} + {{ 'device-profile.lwm2m.sw-update-resource' | translate }} + + + {{ 'device-profile.lwm2m.sw-update-resource-required' | translate }} +
+ device-profile.lwm2m.power-mode + + + + + {{ powerModeLwM2MNamesMap.get(powerModeLwM2MType[powerMode]) }} + + + +
@@ -160,9 +172,9 @@ - + - + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index faf94eccce..389a214d22 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -32,7 +32,7 @@ import { ModelValue, ObjectLwM2M, OBSERVE, - OBSERVE_ATTR_TELEMETRY, + OBSERVE_ATTR_TELEMETRY, powerMode, powerModeNames, RESOURCES, TELEMETRY } from './lwm2m-profile-config.models'; @@ -72,6 +72,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro sortFunction: (key: string, value: object) => object; isFwUpdateStrategy: boolean; isSwUpdateStrategy: boolean; + powerModeLwM2MType = powerMode; + powerModeLwM2MTypes = Object.keys(powerMode); + powerModeLwM2MNamesMap = powerModeNames; get required(): boolean { return this.requiredValue; @@ -97,11 +100,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: [], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], - clientStrategy: [1, []], + clientOnlyObserveAfterConnect: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], - fwUpdateRecourse: [{value: '', disabled: true}, []], - swUpdateRecourse: [{value: '', disabled: true}, []] + fwUpdateResource: [{value: '', disabled: true}, []], + swUpdateResource: [{value: '', disabled: true}, []], + powerMode: [null, Validators.required] }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] @@ -110,11 +114,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro takeUntil(this.destroy$) ).subscribe((fwStrategy) => { if (fwStrategy === 2) { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); this.isFwUpdateStrategy = true; } else { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').disable({emitEvent: false}); this.isFwUpdateStrategy = false; } this.otaUpdateFwStrategyValidate(true); @@ -123,12 +127,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro takeUntil(this.destroy$) ).subscribe((swStrategy) => { if (swStrategy === 2) { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); this.isSwUpdateStrategy = true; } else { this.isSwUpdateStrategy = false; - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').disable({emitEvent: false}); } this.otaUpdateSwStrategyValidate(true); }); @@ -202,10 +206,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { - const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; - const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? - this.configurationValue.clientLwM2mSettings.swUpdateRecourse : ''; + const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ? + this.configurationValue.clientLwM2mSettings.fwUpdateResource : ''; + const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ? + this.configurationValue.clientLwM2mSettings.swUpdateResource : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), @@ -216,15 +220,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: this.configurationValue.bootstrap.servers.binding, bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, - clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, - fwUpdateRecourse: fwResource, - swUpdateRecourse: swResource + fwUpdateResource: fwResource, + swUpdateResource: swResource, + powerMode: this.configurationValue.clientLwM2mSettings.powerMode }, {emitEvent: false}); - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; - this.configurationValue.clientLwM2mSettings.swUpdateRecourse = swResource; + this.configurationValue.clientLwM2mSettings.fwUpdateResource = fwResource; + this.configurationValue.clientLwM2mSettings.swUpdateResource = swResource; this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === 2; this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === 2; this.otaUpdateSwStrategyValidate(); @@ -257,11 +262,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; bootstrapServers.notifIfDisabled = config.notifIfDisabled; bootstrapServers.binding = config.binding; - this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; + this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect; this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; - this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; - this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse; + this.configurationValue.clientLwM2mSettings.fwUpdateResource = config.fwUpdateResource; + this.configurationValue.clientLwM2mSettings.swUpdateResource = config.swUpdateResource; + this.configurationValue.clientLwM2mSettings.powerMode = config.powerMode; this.upDateJsonAllConfig(); } this.updateModel(); @@ -539,20 +545,20 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private otaUpdateFwStrategyValidate(updated = false): void { if (this.isFwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').setValidators([Validators.required]); } else { - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity({emitEvent: updated}); + this.lwm2mDeviceProfileFormGroup.get('fwUpdateResource').updateValueAndValidity({emitEvent: updated}); } private otaUpdateSwStrategyValidate(updated = false): void { if (this.isSwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').setValidators([Validators.required]); } else { - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').clearValidators(); } - this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity({emitEvent: updated}); + this.lwm2mDeviceProfileFormGroup.get('swUpdateResource').updateValueAndValidity({emitEvent: updated}); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 1f2d125d9f..fa9cbd4083 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -128,6 +128,20 @@ export const securityConfigModeNames = new Map( ] ); +export enum powerMode { + PSM = 'PSM', + DRX = 'DRX', + E_DRX = 'E_DRX' +} + +export const powerModeNames = new Map( + [ + [powerMode.PSM, 'Power Saving Mode (PSM)'], + [powerMode.DRX, 'Discontinuous Reception (DRX)'], + [powerMode.E_DRX, 'Extended Discontinuous Reception (eDRX)'] + ] +); + export interface ModelValue { objectIds: string[]; objectsList: ObjectLwM2M[]; @@ -168,11 +182,12 @@ export interface Lwm2mProfileConfigModels { } export interface ClientLwM2mSettings { - clientStrategy: string; + clientOnlyObserveAfterConnect: string; fwUpdateStrategy: number; swUpdateStrategy: number; - fwUpdateRecourse: string; - swUpdateRecourse: string; + fwUpdateResource: string; + swUpdateResource: string; + powerMode: powerMode; } export interface ObservableAttributes { @@ -240,11 +255,12 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientStrategy: '1', + clientOnlyObserveAfterConnect: '1', fwUpdateStrategy: 1, swUpdateStrategy: 1, - fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, - swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE + fwUpdateResource: DEFAULT_FW_UPDATE_RESOURCE, + swUpdateResource: DEFAULT_SW_UPDATE_RESOURCE, + powerMode: powerMode.DRX }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index cb09d84731..f50cec88cf 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1288,11 +1288,12 @@ "sw-update-strategy": "Software update strategy", "sw-update-strategy-package": "Push binary file using Object 9 and Resource 2 (Package)", "sw-update-strategy-package-uri": "Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI)", - "fw-update-recourse": "Firmware update CoAP recourse", - "fw-update-recourse-required": "Firmware update CoAP recourse is required.", - "sw-update-recourse": "Software update CoAP recourse", - "sw-update-recourse-required": "Software update CoAP recourse is required.", - "config-json-tab": "Json Config Profile Device" + "fw-update-resource": "Firmware update CoAP resource", + "fw-update-resource-required": "Firmware update CoAP resource is required.", + "sw-update-resource": "Software update CoAP resource", + "sw-update-resource-required": "Software update CoAP resource is required.", + "config-json-tab": "Json Config Profile Device", + "power-mode": "Power Mode" } }, "dialog": { From 9717ef5ff512d2d9148b65ebb6f7d00558ece4d5 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 16 Jun 2021 18:52:07 +0300 Subject: [PATCH 049/151] Fix DefaultTenantProfileConfiguration model --- ui-ngx/src/app/shared/models/tenant.model.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 4ce7acb5c5..badc48bea4 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -49,8 +49,10 @@ export interface DefaultTenantProfileConfiguration { maxRuleNodeExecutionsPerMessage: number; maxEmails: number; maxSms: number; + maxCreatedAlarms: number; defaultStorageTtlDays: number; + alarmsTtlDays: number; } export type TenantProfileConfigurations = DefaultTenantProfileConfiguration; @@ -81,7 +83,9 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxRuleNodeExecutionsPerMessage: 0, maxEmails: 0, maxSms: 0, - defaultStorageTtlDays: 0 + maxCreatedAlarms: 0, + defaultStorageTtlDays: 0, + alarmsTtlDays: 0 }; configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT}; break; From 08939e23e76605a270972b0ed6046d9fb308d108 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 16 Jun 2021 19:00:45 +0300 Subject: [PATCH 050/151] Refactoring of the Firmware Update --- .../device/data/lwm2m/OtherConfiguration.java | 2 + .../server/LwM2MFirmwareUpdateStrategy.java | 48 ++++ .../server/LwM2MSoftwareUpdateStrategy.java | 48 ++++ .../lwm2m/server/LwM2mServerListener.java | 5 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 3 +- .../server/LwM2mTransportServerHelper.java | 6 +- .../lwm2m/server/LwM2mTransportUtil.java | 215 +++------------ .../lwm2m/server/UpdateResultFw.java | 70 +++++ .../transport/lwm2m/server/UpdateStateFw.java | 56 ++++ .../DefaultLwM2MAttributesService.java | 183 +++++++++---- .../attributes/LwM2MAttributesService.java | 4 +- .../lwm2m/server/client/LwM2mClient.java | 9 +- .../server/client/LwM2mClientContext.java | 5 +- .../server/client/LwM2mClientContextImpl.java | 41 ++- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 65 ++--- .../downlink/TbLwM2MWriteReplaceRequest.java | 6 +- .../ota/DefaultLwM2MOtaUpdateService.java | 248 ++++++++++++++++-- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 99 +++++++ .../lwm2m/server/ota/LwM2MClientOtaState.java | 5 +- .../server/ota/LwM2MOtaUpdateService.java | 13 + .../rpc/DefaultLwM2MRpcRequestHandler.java | 2 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 159 ++--------- .../server/uplink/LwM2mUplinkMsgHandler.java | 4 - 23 files changed, 851 insertions(+), 445 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java index 2bfef74391..829180983b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -23,5 +23,7 @@ public class OtherConfiguration { private Integer fwUpdateStrategy; private Integer swUpdateStrategy; private Integer clientOnlyObserveAfterConnect; + private String fwUpdateRecourse; + private String swUpdateRecourse; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java new file mode 100644 index 0000000000..f111152b76 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server; + +public enum LwM2MFirmwareUpdateStrategy { + OBJ_5_BINARY(1, "ObjectId 5, Binary"), + OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), + OBJ_19_BINARY(3, "ObjectId 19, Binary"); + + public int code; + public String type; + + LwM2MFirmwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java new file mode 100644 index 0000000000..0df3f5ca2d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server; + +public enum LwM2MSoftwareUpdateStrategy { + BINARY(1, "ObjectId 9, Binary"), + TEMP_URL(2, "ObjectId 9, URI"); + + public int code; + public String type; + + LwM2MSoftwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index 36f1e8a903..1c2bb70145 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -27,8 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle import java.util.Collection; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j public class LwM2mServerListener { @@ -93,7 +92,7 @@ public class LwM2mServerListener { @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (registration != null) { - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response); + service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index ebf0a48aba..f39ced6dfc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -33,7 +33,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Optional; @@ -54,7 +53,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener>> futures; private final TransportService transportService; + private final LwM2mTransportServerHelper helper; + private final LwM2mClientContext clientContext; + private final LwM2MTransportServerConfig config; + private final LwM2mUplinkMsgHandler uplinkHandler; + private final LwM2mDownlinkMsgHandler downlinkHandler; + private final LwM2MOtaUpdateService otaUpdateService; @Override public ListenableFuture> getSharedAttributes(LwM2mClient client, Collection keys) { @@ -96,55 +116,114 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { * @param msg - */ @Override - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { -// LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); -// if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { -// log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); -// msg.getSharedUpdatedList().forEach(tsKvProto -> { -// String pathName = tsKvProto.getKv().getKey(); -// String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, pathName); -// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); -// if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { -// this.getInfoFirmwareUpdate(lwM2MClient, null); -// } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { -// this.getInfoSoftwareUpdate(lwM2MClient, null); -// } -// if (pathIdVer != null) { -// ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config -// .getModelProvider()); -// if (resourceModel != null && resourceModel.operations.isWritable()) { -// this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); -// } else { -// log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); -// String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", -// LOG_LWM2M_ERROR, pathIdVer, valueNew); -// this.logToTelemetry(lwM2MClient, logMsg); -// } -// } else if (!isFwSwWords(pathName)) { -// log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); -// String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", -// LOG_LWM2M_ERROR, pathName, valueNew); -// this.logToTelemetry(lwM2MClient, logMsg); -// } -// -// }); -// } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { -// msg.getSharedUpdatedList().forEach(tsKvProto -> { -// String pathName = tsKvProto.getKv().getKey(); -// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); -// if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { -// lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); -// } -// }); -// log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); -// } else if (lwM2MClient == null) { -// log.error("OnAttributeUpdate, lwM2MClient is null"); -// } + public void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { + LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); + if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { + String newFirmwareTitle = null; + String newFirmwareVersion = null; + String newFirmwareUrl = null; + String newSoftwareTitle = null; + String newSoftwareVersion = null; + List otherAttributes = new ArrayList<>(); + for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { + String attrName = tsKvProto.getKv().getKey(); + if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { + newFirmwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { + newFirmwareVersion = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { + newFirmwareUrl = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { + newSoftwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { + newSoftwareVersion = getStrValue(tsKvProto); + } else { + otherAttributes.add(tsKvProto); + } + } + if (newFirmwareTitle != null || newFirmwareVersion != null) { + otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl)); + } + if (newSoftwareTitle != null || newSoftwareVersion != null) { + otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion); + } + if (!otherAttributes.isEmpty()) { + onAttributesUpdate(lwM2MClient, otherAttributes); + } + } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { + msg.getSharedUpdatedList().forEach(tsKvProto -> { + String pathName = tsKvProto.getKv().getKey(); + Object valueNew = getValueFromKvProto(tsKvProto.getKv()); + if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { + lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); + } + }); + } else if (lwM2MClient == null) { + log.error("OnAttributeUpdate, lwM2MClient is null"); + } + } + + /** + * #1.1 If two names have equal path => last time attribute + * #2.1 if there is a difference in values between the current resource values and the shared attribute values + * => send to client Request Update of value (new value from shared attribute) + * and LwM2MClient.delayedRequests.add(path) + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values + * + */ + @Override + public void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos) { + log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); + tsKvProtos.forEach(tsKvProto -> { + String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); + if (pathIdVer != null) { + // #1.1 + if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { + if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); + } + } else { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); + } + } + }); + // #2.1 + lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { + this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), + getValueFromKvProto(tsKvProto.getKv()), pathIdVer); + }); + } + + private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { + if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, lwM2MClient, versionedId)); + } else { + log.error("Failed update resource [{}] [{}]", versionedId, newValue); + String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", + LOG_LWM2M_ERROR, versionedId, newValue); + uplinkHandler.logToTelemetry(lwM2MClient, logMsg); + log.info("Failed update resource [{}] [{}]", versionedId, newValue); + } + } + + /** + * @param pathIdVer - path resource + * @return - value of Resource into format KvProto or null + */ + private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); + if (resourceValue != null) { + ResourceModel.Type currentType = resourceValue.getType(); + ResourceModel.Type expectedType = helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); + return LwM2mValueConverterImpl.getInstance().convertValue(resourceValue.getValue(), currentType, expectedType, + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); + } else { + return null; + } + } + + private String getStrValue(TransportProtos.TsKvProto tsKvProto) { + return tsKvProto.getKv().getStringV(); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java index 008788cb33..b9d7fa9e21 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java @@ -28,5 +28,7 @@ public interface LwM2MAttributesService { void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo); - void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); + void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); + + void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index ec187549c8..21d6ec3d48 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -38,11 +38,9 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Queue; @@ -50,14 +48,13 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; @@ -202,7 +199,7 @@ public class LwM2mClient implements Cloneable { } public Object getResourceValue(String pathRezIdVer, String pathRezId) { - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { return this.resources.get(pathRez).getLwM2mResource().getValue(); } @@ -210,7 +207,7 @@ public class LwM2mClient implements Cloneable { } public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) { - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { return this.resources.get(pathRez).getResourceModel().name; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index 0f2f208b2b..006953af58 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -53,7 +53,10 @@ public interface LwM2mClientContext { LwM2mClient getClientByDeviceId(UUID deviceId); - void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); + String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName); + + String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index b9206ca4df..d60482a0c1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; @@ -25,6 +26,7 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -40,7 +42,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @Slf4j @Service @@ -49,6 +53,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c public class LwM2mClientContextImpl implements LwM2mClientContext { private final LwM2mTransportContext context; + private final LwM2MTransportServerConfig config; private final TbEditableSecurityStore securityStore; private final Map lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); @@ -160,6 +165,28 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClient; } + /** + * Get path to resource from profile equal keyName + * + * @param sessionInfo - + * @param keyName - + * @return - + */ + @Override + public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) { + return getObjectIdByKeyNameFromProfile(getClientBySessionInfo(sessionInfo), keyName); + } + + @Override + public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) { + Lwm2mDeviceProfileTransportConfiguration profile = getProfile(lwM2mClient.getProfileId()); + + return profile.getObserveAttr().getKeyName().entrySet().stream() + .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow( + () -> new IllegalArgumentException(keyName + " is not configured in the device profile!") + ).getKey(); + } + public Registration getRegistration(String registrationId) { return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration(); } @@ -200,7 +227,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> { LwM2mPath pathIds = new LwM2mPath(link.getUrl()); if (!pathIds.isRoot()) { - clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration())); + clientObjects.add(convertObjectIdToVersionedId(link.getUrl(), client.getRegistration())); } }); return (clientObjects.size() > 0) ? clientObjects : null; @@ -211,4 +238,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); } + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config + .getModelProvider()); + Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); + String objectVer = validateObjectVerFromKey(pathIdVer); + return resourceModel != null && (isWritableNotOptional ? + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index b19fa4ac55..b39f6084cd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -23,7 +23,10 @@ import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -62,9 +65,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; @@ -75,8 +78,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.toOtaPackageUpdateStatus; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; @Slf4j @@ -140,8 +143,8 @@ public class LwM2mFwSwUpdate { private void initPathId() { if (FIRMWARE.equals(this.type)) { - this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? - FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? + this.pathPackageId = LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? + FW_PACKAGE_5_ID : LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; this.pathStateId = FW_STATE_ID; this.pathResultId = FW_RESULT_ID; @@ -185,26 +188,26 @@ public class LwM2mFwSwUpdate { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null); - String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); + String targetIdVer = convertObjectIdToVersionedId(this.pathPackageId, this.lwM2MClient.getRegistration()); String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO, LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId); handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { + if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { + } else if (LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { String apiFont = "coap://176.36.143.9:5685"; String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); //TODO: user this.rpcRequest??? TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { + } else if (LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } } else { @@ -269,7 +272,7 @@ public class LwM2mFwSwUpdate { (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR, this.currentVersion); - } else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (updateResultFw != null && updateResultFw > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR, updateResultFw); } @@ -288,7 +291,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteStart() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult; + return UpdateResultFw.INITIAL.code == updateResult; } /** @@ -297,7 +300,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteAfterSuccess() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; + return UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; } /** @@ -306,7 +309,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteAfterError() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; + return UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; } /** @@ -354,7 +357,7 @@ public class LwM2mFwSwUpdate { */ public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : + String value = FIRMWARE.equals(this.type) ? UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { @@ -408,18 +411,18 @@ public class LwM2mFwSwUpdate { public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) { this.infoFwSwUpdate = true; - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathStateId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathResultId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( FW_3_VER_ID, this.lwM2MClient.getRegistration())); - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || - LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || + if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || + LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || SOFTWARE.equals(this.type)) { - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathVerId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathNameId, this.lwM2MClient.getRegistration())); } this.pendingInfoRequestsStart.forEach(versionedId -> { @@ -442,7 +445,7 @@ public class LwM2mFwSwUpdate { public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, Registration registration, String path, int value) { if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { + if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { if (DOWNLOADED.name().equals(this.getStateUpdate()) && this.conditionalFwExecuteStart()) { this.executeFwSwWare(handler, request); @@ -455,23 +458,23 @@ public class LwM2mFwSwUpdate { } } } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { - if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { - String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); + if (this.currentId != null && (convertObjectIdToVersionedId(FW_STATE_ID, registration).equals(path))) { + String state = toOtaPackageUpdateStatus(UpdateStateFw.fromStateFwByCode(value)).name(); if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { this.stateUpdate = state; this.sendSateOnThingsBoard(handler); } - if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) { + if (value == UpdateStateFw.DOWNLOADED.code) { this.executeFwSwWare(handler, request); } handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value); } - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { - if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { + if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { + if (this.currentId != null && value == UpdateResultFw.INITIAL.code) { this.setStateUpdate(INITIATED.name()); - } else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (this.currentId != null && value == UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(UPDATED.name()); - } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (value > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(FAILED.name()); } this.sendSateOnThingsBoard(handler); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java index 007aa1d3f8..f4b1059944 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java @@ -17,17 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + @Getter + private final ContentFormat contentFormat; @Getter private final Object value; @Builder - private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, Object value) { + private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, ContentFormat contentFormat, Object value) { super(versionedId, timeout); + this.contentFormat = contentFormat; this.value = value; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 0b862ff725..68d7d76b73 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -17,21 +17,50 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.request.ContentFormat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j @Service @@ -39,45 +68,228 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttribute @RequiredArgsConstructor public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { + public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION); + public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); + public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); + public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); + public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); + public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); + + private static final String FW_PACKAGE_5_ID = "/5/0/0"; + private static final String FW_URL_ID = "/5/0/1"; + private static final String FW_EXECUTE_ID = "/5/0/2"; private static final String FW_NAME_ID = "/5/0/6"; private static final String FW_VER_ID = "/5/0/7"; private static final String SW_NAME_ID = "/9/0/0"; private static final String SW_VER_ID = "/9/0/1"; - private final Map fwStates = new ConcurrentHashMap<>(); - private final Map swStates = new ConcurrentHashMap<>(); + private final Map fwStates = new ConcurrentHashMap<>(); + private final Map swStates = new ConcurrentHashMap<>(); - private final LwM2MAttributesService attributesService; private final TransportService transportService; private final LwM2mClientContext clientContext; + private final LwM2MTransportServerConfig config; + private final LwM2mUplinkMsgHandler uplinkHandler; + private final LwM2mDownlinkMsgHandler downlinkHandler; + private final OtaPackageDataCache otaPackageDataCache; + private final LwM2mTransportServerHelper helper; + private ExecutorService executor; + + @Autowired + @Lazy + private LwM2MAttributesService attributesService; + + @PostConstruct + public void init() { + //TODO: define parallelism in constant + executor = ThingsBoardExecutors.newWorkStealingPool(4, "LwM2M OTA Updates"); + } @Override public void init(LwM2mClient client) { + //TODO: add locks by client fwInfo. //TODO: check that the client supports FW and SW by checking the supported objects in the model. List attributesToFetch = new ArrayList<>(); - if (client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)) { - LwM2MClientOtaState fwState = getOrInitFwSate(client); - attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE)); - attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION)); + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setSupported(client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)); + if (fwInfo.isSupported()) { + attributesToFetch.add(FIRMWARE_TITLE); + attributesToFetch.add(FIRMWARE_VERSION); + attributesToFetch.add(FIRMWARE_URL); + } + + if (!attributesToFetch.isEmpty()) { + var future = attributesService.getSharedAttributes(client, attributesToFetch); + DonAsynchron.withCallback(future, attrs -> { + if (fwInfo.isSupported()) { + Optional newFirmwareTitle = getAttributeValue(attrs, FIRMWARE_TITLE); + Optional newFirmwareVersion = getAttributeValue(attrs, FIRMWARE_VERSION); + Optional newFirmwareUrl = getAttributeValue(attrs, FIRMWARE_URL); + if (newFirmwareTitle.isPresent() && newFirmwareVersion.isPresent()) { + onTargetFirmwareUpdate(client, newFirmwareTitle.get(), newFirmwareVersion.get(), newFirmwareUrl); + } + } + }, throwable -> { + if (fwInfo.isSupported()) { + fwInfo.setTargetFetchFailure(true); + } + }, executor); + } + } + + @Override + public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl); + startFirmwareUpdateIfNeeded(client, fwInfo); + } + + @Override + public void onCurrentFirmwareNameUpdate(LwM2mClient client, String name) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setCurrentName(name); + } + + @Override + public void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setCurrentVersion(version); + } + + @Override + public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + UpdateStateFw newState = UpdateStateFw.fromStateFwByCode(state.intValue()); + if (UpdateStateFw.DOWNLOADED.equals(newState)) { + executeFwUpdate(client); + } + fwInfo.setUpdateState(newState); + sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(newState)); + } + + @Override + public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); + sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(result)); + if (result.isAgain && fwInfo.getRetryAttempts() <= 2) { + fwInfo.setRetryAttempts(fwInfo.getRetryAttempts() + 1); + startFirmwareUpdateIfNeeded(client, fwInfo); + } else { + fwInfo.setUpdateResult(result); } + } - if (client.isValidObjectVersion(SW_NAME_ID) || client.isValidObjectVersion(SW_VER_ID)) { - LwM2MClientOtaState swState = getOrInitSwSate(client); - attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE)); - attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION)); + @Override + public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion) { + + } + + private void startFirmwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { + if (fwInfo.isUpdateRequired()) { + if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { + log.info("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); + startFirmwareUpdateUsingUrl(client, fwInfo.getTargetUrl()); + } else { + startFirmwareUpdateUsingBinary(client, fwInfo); + } } + } + + private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { + String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); + TbLwM2MWriteUpdateRequest request = TbLwM2MWriteUpdateRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteUpdateRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, client, targetIdVer)); + } + + public void startFirmwareUpdateUsingBinary(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { + String versionedId = convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client.getRegistration()); + this.transportService.process(client.getSession(), createOtaPackageRequestMsg(client.getSession(), OtaPackageType.FIRMWARE.name()), + new TransportServiceCallback<>() { + @Override + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) + && response.getType().equals(OtaPackageType.FIRMWARE.name())) { + UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); + var strategy = fwInfo.getStrategy(); + switch (strategy) { + case OBJ_5_BINARY: + byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); + TbLwM2MWriteReplaceRequest writeRequest = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) + .value(firmwareChunk).contentFormat(ContentFormat.OPAQUE) + .timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId)); + break; + case OBJ_5_TEMP_URL: + startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString()); + break; + default: + //TODO: send log to telemetry + } + } else { + //TODO: send log to telemetry + } + } - var future = attributesService.getSharedAttributes(client, attributesToFetch); + @Override + public void onError(Throwable e) { + log.trace("Failed to process firmwareUpdate ", e); + } + }); } - private LwM2MClientOtaState getOrInitFwSate(LwM2mClient client) { - //TODO: fetch state from the cache. - return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(TransportProtos.SessionInfoProto sessionInfo, String nameFwSW) { + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setType(nameFwSW) + .build(); + } + + private void executeFwUpdate(LwM2mClient client) { + TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(FW_EXECUTE_ID).timeout(config.getTimeout()).build(); + downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(uplinkHandler, client, FW_EXECUTE_ID)); + } + + private Optional getAttributeValue(List attrs, String keyName) { + for (TransportProtos.TsKvProto attr : attrs) { + if (keyName.equals(attr.getKv().getKey())) { + if (attr.getKv().getType().equals(TransportProtos.KeyValueType.STRING_V)) { + return Optional.of(attr.getKv().getStringV()); + } else { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + private LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { + //TODO: fetch state from the cache or DB. + return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { + var profile = clientContext.getProfile(client.getProfileId()); + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(), + profile.getClientLwM2mSettings().getFwUpdateRecourse()); + }); + } + + private LwM2MClientOtaInfo getOrInitSwInfo(LwM2mClient client) { + //TODO: fetch state from the cache or DB. + return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> { + var profile = clientContext.getProfile(client.getProfileId()); + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy(), profile.getClientLwM2mSettings().getSwUpdateRecourse()); + }); + } - private LwM2MClientOtaState getOrInitSwSate(LwM2mClient client) { - //TODO: fetch state from the cache. - return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + private void sendStateUpdateToTelemetry(LwM2mClient client, LwM2MClientOtaInfo fwInfo, OtaPackageUpdateStatus status) { + List result = new ArrayList<>(); + TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(fwInfo.getType(), STATE)); + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(status.name()); + result.add(kvProto.build()); + helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java new file mode 100644 index 0000000000..26f7abed4f --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -0,0 +1,99 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota; + +import lombok.Data; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; + +import java.util.Optional; + +@Data +public class LwM2MClientOtaInfo { + + private final String endpoint; + private final OtaPackageType type; + + private String baseUrl; + private boolean supported; + + private boolean targetFetchFailure; + private String targetName; + private String targetVersion; + private String targetUrl; + + private boolean currentFetchFailure; + private String currentName; + private String currentVersion; + + //TODO: use value from device if applicable; + private LwM2MFirmwareUpdateStrategy strategy; + private UpdateStateFw updateState; + private UpdateResultFw updateResult; + + private String failedPackageId; + private int retryAttempts; + + public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { + this.endpoint = endpoint; + this.type = type; + this.strategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); + this.baseUrl = baseUrl; + } + + public void updateTarget(String targetName, String targetVersion, Optional newFirmwareUrl) { + this.targetName = targetName; + this.targetVersion = targetVersion; + this.targetUrl = newFirmwareUrl.orElse(null); + } + + public boolean isUpdateRequired() { + if (StringUtils.isEmpty(targetName) || + StringUtils.isEmpty(targetVersion) || + (StringUtils.isEmpty(currentName) && StringUtils.isEmpty(currentVersion))) { + return false; + } else { + String targetPackageId = getPackageId(targetName, targetVersion); + String currentPackageId = getPackageId(currentName, currentVersion); + if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { + return false; + } else { + return !targetPackageId.equals(currentPackageId); + } + } + } + + public void setUpdateResult(UpdateResultFw updateResult) { + this.updateResult = updateResult; + switch (updateResult) { + case INITIAL: + break; + case UPDATE_SUCCESSFULLY: + retryAttempts = 0; + break; + default: + failedPackageId = getPackageId(targetName, targetVersion); + break; + } + } + + private static String getPackageId(String name, String version) { + return (StringUtils.isNotEmpty(name) ? name : "") + (StringUtils.isNotEmpty(version) ? version : ""); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java index 43dd874a09..b4201f3abd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java @@ -15,5 +15,8 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota; -public class LwM2MClientOtaState { +public enum LwM2MClientOtaState { + + IDLE, IN_PROGRESS, SUCCESS, FAILED + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index 9dcbab5d9e..e1bde7df8b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -17,8 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.Optional; + public interface LwM2MOtaUpdateService { void init(LwM2mClient client); + void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl); + + void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion); + + void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); + + void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version); + + void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state); + + void onCurrentFirmwareResultUpdate(LwM2mClient client, Long result); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index b2c8f73ac8..533458ce76 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -238,7 +238,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { IdOrKeyRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), IdOrKeyRequest.class); String targetId; if (StringUtils.isNotEmpty(requestParams.getKey())) { - targetId = uplinkHandler.getObjectIdByKeyNameFromProfile(client, requestParams.getKey()); + targetId = clientContext.getObjectIdByKeyNameFromProfile(client, requestParams.getKey()); } else if (StringUtils.isNotEmpty(requestParams.getId())) { targetId = requestParams.getId(); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index ec5fbbb6dd..97fe273857 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -68,23 +68,20 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; @@ -109,8 +106,10 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; @@ -119,9 +118,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @Slf4j @@ -148,9 +146,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public final Map firmwareUpdateState; - public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MAttributesService attributesService, LwM2MOtaUpdateService otaService, + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, + @Lazy LwM2MOtaUpdateService otaService, + @Lazy LwM2MAttributesService attributesService, @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, @@ -561,21 +561,16 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path) { Registration registration = lwM2MClient.getRegistration(); if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { - /** version != null - * set setClient_fw_info... = value - **/ - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); - } - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); - } - - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || - (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(this, clientContext); - log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); - fwUpdate.updateStateOta(this, defaultLwM2MDownlinkMsgHandler, registration, path, ((Long) lwM2mResource.getValue()).intValue()); + if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, registration))) { + otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, registration))) { + otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_5_VER_ID, registration))) { + otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, registration))) { + otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, registration))) { + otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } this.updateAttrTelemetry(registration, Collections.singleton(path)); } else { @@ -684,7 +679,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { String resourceName = names.get(pathIdVer); if (resourceName != null && !resourceName.isEmpty()) { try { - LwM2mResource resourceValue = getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); if (resourceValue != null) { ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); @@ -720,38 +715,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return null; } - /** - * @param pathIdVer - path resource - * @return - value of Resource into format KvProto or null - */ - private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { - LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); - if (resourceValue != null) { - ResourceModel.Type currentType = resourceValue.getType(); - ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); - return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, - new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); - } else { - return null; - } - } - - /** - * @param lwM2MClient - - * @param path - - * @return - return value of Resource by idPath - */ - private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { - LwM2mResource lwm2mResourceValue = null; - ResourceValue resourceValue = lwM2MClient.getResources().get(path); - if (resourceValue != null) { - if (new LwM2mPath(fromVersionedIdToObjectId(path)).isResource()) { - lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); - } - } - return lwm2mResourceValue; - } - @Override public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { if (request.getNode() instanceof LwM2mResource) { @@ -890,19 +853,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { - if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { - TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(this, lwM2MClient, versionedId)); - } else { - log.error("Failed update resource [{}] [{}]", versionedId, newValue); - String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", - LOG_LWM2M_ERROR, versionedId, newValue); - this.logToTelemetry(lwM2MClient, logMsg); - log.info("Failed update resource [{}] [{}]", versionedId, newValue); - } - } - /** * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) * config attr/telemetry... in profile @@ -912,59 +862,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); } - /** - * Get path to resource from profile equal keyName - * - * @param sessionInfo - - * @param keyName - - * @return - - */ - @Override - public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) { - return getObjectIdByKeyNameFromProfile(clientContext.getClientBySessionInfo(sessionInfo), keyName); - } - - @Override - public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) { - Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2mClient.getProfileId()); - - return profile.getObserveAttr().getKeyName().entrySet().stream() - .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow( - () -> new IllegalArgumentException(keyName + " is not configured in the device profile!") - ).getKey(); - } - - /** - * #1.1 If two names have equal path => last time attribute - * #2.1 if there is a difference in values between the current resource values and the shared attribute values - * => send to client Request Update of value (new value from shared attribute) - * and LwM2MClient.delayedRequests.add(path) - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values - * - * @param tsKvProtos - */ - public void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos) { - log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); - tsKvProtos.forEach(tsKvProto -> { - String pathIdVer = this.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); - if (pathIdVer != null) { - // #1.1 - if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { - if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { - lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); - } - } else { - lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); - } - } - }); - // #2.1 - lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { - this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), - getValueFromKvProto(tsKvProto.getKv()), pathIdVer); - }); - } - /** * @param lwM2MClient - * @return SessionInfoProto - @@ -1017,7 +914,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { keysToFetch.removeAll(OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS); keysToFetch.removeAll(OtaPackageUtil.ALL_SW_ATTRIBUTE_KEYS); DonAsynchron.withCallback(attributesService.getSharedAttributes(lwM2MClient, keysToFetch), - v -> onAttributesUpdate(lwM2MClient, v), + v -> attributesService.onAttributesUpdate(lwM2MClient, v), t -> log.error("[{}] Failed to get attributes", lwM2MClient.getEndpoint(), t), registrationExecutor); } @@ -1045,7 +942,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { fwUpdate.setCurrentTitle(response.getTitle()); fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); // if (rpcRequest == null) { - fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); + fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // } else { // fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); // } @@ -1059,7 +956,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace(msgError); // if (rpcRequest != null) { - //TODO: refactor + //TODO: refactor // sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); // } } @@ -1091,7 +988,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // if (rpcRequest == null) { - lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); + lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // } else { // lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); // } @@ -1124,16 +1021,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return profile.getObserveAttr().getKeyName(); } - private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { - ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config - .getModelProvider()); - Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); - String objectVer = validateObjectVerFromKey(pathIdVer); - return resourceModel != null && (isWritableNotOptional ? - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); - } - public LwM2MTransportServerConfig getConfig() { return this.config; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 041af97474..517db388e4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -60,9 +60,5 @@ public interface LwM2mUplinkMsgHandler { void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); - String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String name); - - String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); - LwM2MTransportServerConfig getConfig(); } From 079ae5fd8f17bb7b6f788372e7734bc5e03a8796 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Wed, 16 Jun 2021 23:06:52 +0300 Subject: [PATCH 051/151] UI: Refactoring resource: add help link; improvement text and view --- .../resources-library-table-config.resolve.ts | 10 +++---- .../resource/resources-library.component.html | 24 ++++++++++++++--- .../resource/resources-library.component.ts | 27 ++++++++++++++----- ui-ngx/src/app/shared/models/constants.ts | 1 + .../src/app/shared/models/resource.models.ts | 4 +-- .../assets/locale/locale.constant-en_US.json | 7 +++-- .../assets/locale/locale.constant-zh_CN.json | 1 - 7 files changed, 54 insertions(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts index 089d4b3d82..96745fa47a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts @@ -69,10 +69,10 @@ export class ResourcesLibraryTableConfigResolver implements Resolve true, - onAction: ($event, entity) => this.exportResource($event, entity) + onAction: ($event, entity) => this.downloadResource($event, entity) } ); @@ -118,7 +118,7 @@ export class ResourcesLibraryTableConfigResolver implements Resolve): boolean { switch (action.action) { - case 'uploadResource': - this.exportResource(action.event, action.entity); + case 'downloadResource': + this.downloadResource(action.event, action.entity); return true; } return false; diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index 5b98fa9c61..1f33d63776 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -18,16 +18,26 @@
+
+ +
@@ -47,7 +57,7 @@ {{ 'resource.title-required' | translate }} - +
+ + resource.file-name + + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts index ce26499102..50d565ccc0 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts @@ -30,6 +30,7 @@ import { ResourceTypeTranslationMap } from '@shared/models/resource.models'; import { pairwise, startWith, takeUntil } from 'rxjs/operators'; +import { ActionNotificationShow } from "@core/notification/notification.actions"; @Component({ selector: 'tb-resources-library', @@ -88,26 +89,29 @@ export class ResourcesLibraryComponent extends EntityComponent impleme } buildForm(entity: Resource): FormGroup { - return this.fb.group( + const form = this.fb.group( { + title: [entity ? entity.title : '', []], resourceType: [{ value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, - disabled: this.isEdit + disabled: !this.isAdd }, [Validators.required]], - data: [entity ? entity.data : null, [Validators.required]], fileName: [entity ? entity.fileName : null, [Validators.required]], - title: [entity ? entity.title : '', []] } ); + if (this.isAdd) { + form.addControl('data', this.fb.control(null, Validators.required)); + } + return form } updateForm(entity: Resource) { - this.entityForm.patchValue({resourceType: entity.resourceType}); if (this.isEdit) { this.entityForm.get('resourceType').disable({emitEvent: false}); + this.entityForm.get('fileName').disable({emitEvent: false}); } this.entityForm.patchValue({ - data: entity.data, + resourceType: entity.resourceType, fileName: entity.fileName, title: entity.title }); @@ -132,4 +136,15 @@ export class ResourcesLibraryComponent extends EntityComponent impleme convertToBase64File(data: string): string { return window.btoa(data); } + + onResourceIdCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('resource.idCopiedMessage'), + type: 'success', + duration: 750, + verticalPosition: 'bottom', + horizontalPosition: 'right' + })); + } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index b72e2b196d..56347f1261 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -120,6 +120,7 @@ export const HelpLinks = { entityViews: helpBaseUrl + '/docs/user-guide/ui/entity-views', entitiesImport: helpBaseUrl + '/docs/user-guide/bulk-provisioning', rulechains: helpBaseUrl + '/docs/user-guide/ui/rule-chains', + resources: helpBaseUrl + '/docs/user-guide/ui/resources', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', otaUpdates: helpBaseUrl + '/docs/user-guide/ui/ota-updates', widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 082d09ac24..9c478cca0d 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -61,6 +61,6 @@ export interface Resource extends ResourceInfo { } export interface Resources extends ResourceInfo { - data: string|string[]; - fileName: string|string[]; + data: Array; + fileName: Array; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ec084b8a3a..172cc2033d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2309,20 +2309,23 @@ }, "resource": { "add": "Add Resource", + "copyId": "Copy resource Id", "delete": "Delete resource", "delete-resource-text": "Be careful, after the confirmation the resource will become unrecoverable.", "delete-resource-title": "Are you sure you want to delete the resource '{{resourceTitle}}'?", "delete-resources-action-title": "Delete { count, plural, 1 {1 resource} other {# resources} }", "delete-resources-text": "Be careful, after the confirmation all selected resources will be removed.", "delete-resources-title": "Are you sure you want to delete { count, plural, 1 {1 resource} other {# resources} }?", + "download": "Download resource", "drop-file": "Drop a resource file or click to select a file to upload.", "empty": "Resource is empty", - "export": "Export resource", + "file-name": "File name", + "idCopiedMessage": "Resource Id has been copied to clipboard", "no-resource-matching": "No resource matching '{{widgetsBundle}}' were found.", "no-resource-text": "No resources found", "open-widgets-bundle": "Open widgets bundle", "resource": "Resource", - "resource-library-details": "Resource library details", + "resource-library-details": "Resource details", "resource-type": "Resource type", "resources-library": "Resources library", "search": "Search resources", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 6a60db96fc..cc23f5fc78 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -1994,7 +1994,6 @@ "delete-resources-title": "确定要删除 { count, plural, 1 {# 个资源} other {# 个资源} }?", "drop-file": "拖拽资源文件或单击以选择要上传的文件。", "empty": "资源为空", - "export": "导出资源", "no-resource-matching": "找不到与 '{{widgetsBundle}}' 匹配的资源。", "no-resource-text": "找不到资源", "open-widgets-bundle": "打开部件库", From cda6f3678b8cfe7a0046d75ff5daeae3287b8312 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 07:27:42 +0300 Subject: [PATCH 052/151] js-executor: fixed license header --- msa/js-executor/api/httpServer.js | 36 +++++++++---------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/msa/js-executor/api/httpServer.js b/msa/js-executor/api/httpServer.js index 996638c7c3..4948c45f61 100644 --- a/msa/js-executor/api/httpServer.js +++ b/msa/js-executor/api/httpServer.js @@ -1,34 +1,18 @@ /* - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * Copyright © 2016-2021 The Thingsboard Authors * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - const config = require('config'), logger = require('../config/logger')._logger('httpServer'), express = require('express'); From c07379812b213f3ca249da31285a00124d6345bd Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 07:28:53 +0300 Subject: [PATCH 053/151] js-executor: updated yarn.lock --- msa/js-executor/yarn.lock | 361 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 347 insertions(+), 14 deletions(-) diff --git a/msa/js-executor/yarn.lock b/msa/js-executor/yarn.lock index 404c059c23..0092372b69 100644 --- a/msa/js-executor/yarn.lock +++ b/msa/js-executor/yarn.lock @@ -418,6 +418,14 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + agent-base@6: version "6.0.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" @@ -487,6 +495,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -621,6 +634,22 @@ bluebird@^3.5.2: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + boxen@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" @@ -682,6 +711,11 @@ byline@^5.0.0: resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -838,6 +872,28 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -867,6 +923,13 @@ dateformat@1.0.2-1.2.3: dependencies: ms "^2.1.1" +debug@2.6.9, debug@^2.2.0, debug@~2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@4, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -874,13 +937,6 @@ debug@4, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@^2.2.0, debug@~2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -913,6 +969,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -962,6 +1028,11 @@ ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: dependencies: safe-buffer "^5.0.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -977,6 +1048,11 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -994,6 +1070,11 @@ escape-goat@^2.0.0: resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escodegen@^1.14.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -1021,6 +1102,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -1041,6 +1127,42 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -1119,6 +1241,19 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -1155,6 +1290,16 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + from2@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -1384,6 +1529,28 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -1401,6 +1568,13 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ieee754@1.1.13, ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" @@ -1431,7 +1605,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1449,6 +1623,11 @@ into-stream@^5.1.1: from2 "^2.3.0" p-is-promise "^3.0.0" +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" @@ -1764,11 +1943,26 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + micromatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" @@ -1782,6 +1976,11 @@ mime-db@1.44.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" @@ -1789,6 +1988,18 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.44.0" +mime-types@~2.1.24: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mime@^2.2.0: version "2.4.6" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" @@ -1833,6 +2044,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1846,6 +2062,11 @@ multistream@^2.1.1: inherits "^2.0.1" readable-stream "^2.0.5" +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + node-fetch@^2.3.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -1899,6 +2120,13 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1974,6 +2202,11 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -1984,6 +2217,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -2079,6 +2317,14 @@ protobufjs@^6.8.6, protobufjs@^6.9.0: "@types/node" "^13.7.0" long "^4.0.0" +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -2114,6 +2360,11 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -2129,6 +2380,21 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -2292,17 +2558,17 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -2339,11 +2605,45 @@ semver@^7.1.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -2391,6 +2691,11 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + stream-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -2513,6 +2818,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" @@ -2581,6 +2891,14 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -2636,6 +2954,11 @@ universalify@^1.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + update-notifier@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" @@ -2705,6 +3028,11 @@ util@^0.11.1: dependencies: inherits "2.0.3" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + uuid-parse@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/uuid-parse/-/uuid-parse-1.1.0.tgz#7061c5a1384ae0e1f943c538094597e1b5f3a65b" @@ -2735,6 +3063,11 @@ validator@^9.4.1: resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" integrity sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA== +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" From d117cec09e25c52ea69807a4d4db051505a3b934 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 07:42:21 +0300 Subject: [PATCH 054/151] js-executor: partitions_consumed_concurrently marked as experimental --- msa/js-executor/config/custom-environment-variables.yml | 2 +- msa/js-executor/config/default.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 4185e65835..fdf261e25c 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -29,7 +29,7 @@ kafka: acks: "TB_KAFKA_ACKS" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge batch_size: "TB_KAFKA_BATCH_SIZE" # for producer linger_ms: "TB_KAFKA_LINGER_MS" # for producer - partitions_consumed_concurrently: "TB_KAFKA_PARTITIONS_CONSUMED_CONCURRENTLY" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency + partitions_consumed_concurrently: "TB_KAFKA_PARTITIONS_CONSUMED_CONCURRENTLY" # (EXPERIMENTAL) increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency requestTimeout: "TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS" compression: "TB_QUEUE_KAFKA_COMPRESSION" # gzip or uncompressed topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index 190cf097ed..7a6e7c2469 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -29,7 +29,7 @@ kafka: acks: "1" # -1 = all; 0 = no acknowledgments; 1 = only waits for the leader to acknowledge batch_size: "128" # for producer linger_ms: "1" # for producer - partitions_consumed_concurrently: "1" # increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency + partitions_consumed_concurrently: "1" # (EXPERIMENTAL) increase this value if you are planning to handle more than one partition (scale up, scale down) - this will decrease the latency requestTimeout: "30000" # The default value in kafkajs is: 30000 compression: "gzip" # gzip or uncompressed topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1" From a3b31337cae7f0b639285855fd1cc050d05cfda4 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 5 May 2021 09:09:12 +0300 Subject: [PATCH 055/151] sync method replaced with async executeGenerateAsync for ScriptEngine api. affected Generator node, ruleChainController (merged with ce) --- .../controller/RuleChainController.java | 4 +- .../service/script/RemoteJsInvokeService.java | 8 ++- .../script/RuleNodeJsScriptEngine.java | 19 ++++--- .../rule/engine/api/ScriptEngine.java | 2 +- .../rule/engine/debug/TbMsgGeneratorNode.java | 50 ++++++++++++------- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index d5fcc2706f..3b9af2f0f2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -74,6 +74,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @@ -86,6 +87,7 @@ public class RuleChainController extends BaseController { public static final String RULE_NODE_ID = "ruleNodeId"; private static final ObjectMapper objectMapper = new ObjectMapper(); + public static final int TIMEOUT = 20; @Autowired private InstallScripts installScripts; @@ -391,7 +393,7 @@ public class RuleChainController extends BaseController { output = msgToOutput(engine.executeUpdate(inMsg)); break; case "generate": - output = msgToOutput(engine.executeGenerate(inMsg)); + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); break; case "filter": boolean result = engine.executeFilter(inMsg); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index 334a471973..1bc9203333 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.script; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -26,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.springframework.util.StopWatch; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.queue.TbQueueRequestTemplate; @@ -161,6 +161,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Override protected ListenableFuture doInvokeFunction(UUID scriptId, String functionName, Object[] args) { + log.trace("doInvokeFunction js-request for uuid {} with timeout {}ms", scriptId, maxRequestsTimeout); String scriptBody = scriptIdToBodysMap.get(scriptId); if (scriptBody == null) { return Futures.immediateFailedFuture(new RuntimeException("No script body found for scriptId: [" + scriptId + "]!")); @@ -180,6 +181,9 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { .setInvokeRequest(jsRequestBuilder.build()) .build(); + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ListenableFuture> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper)); if (maxRequestsTimeout > 0) { future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService); @@ -201,6 +205,8 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { } }, callbackExecutor); return Futures.transform(future, response -> { + stopWatch.stop(); + log.trace("doInvokeFunction js-response took {}ms for uuid {}", stopWatch.getTotalTimeMillis(), response.getKey()); JsInvokeProtos.JsInvokeResponse invokeResult = response.getValue().getInvokeResponse(); if (invokeResult.getSuccess()) { return invokeResult.getResult(); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index 066ce71a58..c688d6978d 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -102,7 +102,6 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); return TbMsg.transformMsg(msg, newMessageType, msg.getOriginator(), newMetadata, newData); } catch (Throwable th) { - th.printStackTrace(); throw new RuntimeException("Failed to unbind message data from javascript result", th); } } @@ -141,13 +140,16 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S } @Override - public TbMsg executeGenerate(TbMsg prevMsg) throws ScriptException { - JsonNode result = executeScript(prevMsg); - if (!result.isObject()) { - log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); - } - return unbindMsg(result, prevMsg); + public ListenableFuture executeGenerateAsync(TbMsg prevMsg) { + log.trace("execute generate async, prevMsg {}", prevMsg); + return Futures.transformAsync(executeScriptAsync(prevMsg), result -> { + if (!result.isObject()) { + log.warn("Wrong result type: {}", result.getNodeType()); + throw new ScriptException("Wrong result type: " + result.getNodeType()); + } + return Futures.immediateFuture(unbindMsg(result, prevMsg)); + }, MoreExecutors.directExecutor()); + } @Override @@ -234,6 +236,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S } private ListenableFuture executeScriptAsync(TbMsg msg) { + log.trace("execute script async, msg {}", msg); String[] inArgs = prepareArgs(msg); return Futures.transformAsync(sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]), o -> { diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java index 49420e8feb..a5dcf535e4 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java @@ -29,7 +29,7 @@ public interface ScriptEngine { ListenableFuture> executeUpdateAsync(TbMsg msg); - TbMsg executeGenerate(TbMsg prevMsg) throws ScriptException; + ListenableFuture executeGenerateAsync(TbMsg prevMsg); boolean executeFilter(TbMsg msg) throws ScriptException; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index b5a47b4a0d..8fa4b97063 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -15,9 +15,12 @@ */ package org.thingsboard.rule.engine.debug; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; +import org.thingsboard.common.util.TbStopWatch; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.TbContext; @@ -35,6 +38,7 @@ import org.thingsboard.server.common.msg.queue.ServiceQueue; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static org.thingsboard.common.util.DonAsynchron.withCallback; import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @@ -64,10 +68,11 @@ public class TbMsgGeneratorNode implements TbNode { private EntityId originatorId; private UUID nextTickId; private TbMsg prevMsg; - private volatile boolean initialized; + private final AtomicBoolean initialized = new AtomicBoolean(false); @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + log.trace("init generator with config {}", configuration); this.config = TbNodeUtils.convert(configuration, TbMsgGeneratorNodeConfiguration.class); this.delay = TimeUnit.SECONDS.toMillis(config.getPeriodInSeconds()); this.currentMsgCount = 0; @@ -81,35 +86,39 @@ public class TbMsgGeneratorNode implements TbNode { @Override public void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) { + log.trace("onPartitionChangeMsg, PartitionChangeMsg {}, config {}", msg, config); updateGeneratorState(ctx); } private void updateGeneratorState(TbContext ctx) { + log.trace("updateGeneratorState, config {}", config); if (ctx.isLocalEntity(originatorId)) { - if (!initialized) { - initialized = true; + if (initialized.compareAndSet(false, true)) { this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType"); scheduleTickMsg(ctx); } - } else if (initialized) { - initialized = false; + } else if (initialized.compareAndSet(true, false)) { destroy(); } } @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (initialized && msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { + log.trace("onMsg, config {}, msg {}", config, msg); + if (initialized.get() && msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { + TbStopWatch sw = TbStopWatch.startNew(); withCallback(generate(ctx, msg), m -> { - if (initialized && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { + log.trace("onMsg onSuccess callback, took {}ms, config {}, msg {}", sw.stopAndGetTotalTimeMillis(), config, msg); + if (initialized.get() && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { ctx.enqueueForTellNext(m, SUCCESS); scheduleTickMsg(ctx); currentMsgCount++; } }, t -> { - if (initialized && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { + log.warn("onMsg onFailure callback, took {}ms, config {}, msg {}", sw.stopAndGetTotalTimeMillis(), config, msg); + if (initialized.get() && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { ctx.tellFailure(msg, t); scheduleTickMsg(ctx); currentMsgCount++; @@ -119,6 +128,7 @@ public class TbMsgGeneratorNode implements TbNode { } private void scheduleTickMsg(TbContext ctx) { + log.trace("scheduleTickMsg, config {}", config); long curTs = System.currentTimeMillis(); if (lastScheduledTs == 0L) { lastScheduledTs = curTs; @@ -131,22 +141,26 @@ public class TbMsgGeneratorNode implements TbNode { } private ListenableFuture generate(TbContext ctx, TbMsg msg) { - return ctx.getJsExecutor().executeAsync(() -> { - if (prevMsg == null) { - prevMsg = ctx.newMsg(ServiceQueue.MAIN, "", originatorId, msg.getCustomerId(), new TbMsgMetaData(), "{}"); - } - if (initialized) { - ctx.logJsEvalRequest(); - TbMsg generated = jsEngine.executeGenerate(prevMsg); + log.trace("generate, config {}", config); + if (prevMsg == null) { + prevMsg = ctx.newMsg(ServiceQueue.MAIN, "", originatorId, msg.getCustomerId(), new TbMsgMetaData(), "{}"); + } + if (initialized.get()) { + ctx.logJsEvalRequest(); + return Futures.transformAsync(jsEngine.executeGenerateAsync(prevMsg), generated -> { + log.trace("generate process response, generated {}, config {}", generated, config); ctx.logJsEvalResponse(); prevMsg = ctx.newMsg(ServiceQueue.MAIN, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData()); - } - return prevMsg; - }); + return Futures.immediateFuture(prevMsg); + }, MoreExecutors.directExecutor()); + } + return Futures.immediateFuture(prevMsg); + } @Override public void destroy() { + log.trace("destroy, config {}", config); prevMsg = null; if (jsEngine != null) { jsEngine.destroy(); From 6888a1b349de234db1f6f3a8b81326fb34f438b1 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 5 May 2021 08:22:38 +0300 Subject: [PATCH 056/151] new utility class tbStopWatch added to improve performance measurement experience --- common/util/pom.xml | 5 ++ .../thingsboard/common/util/TbStopWatch.java | 62 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java diff --git a/common/util/pom.xml b/common/util/pom.xml index 6f66da5790..128bc478d5 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -36,6 +36,11 @@ + + org.springframework + spring-core + ${spring.version} + com.google.guava guava diff --git a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java new file mode 100644 index 0000000000..b7dbd0052c --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java @@ -0,0 +1,62 @@ +/** + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ +package org.thingsboard.common.util; + +import org.springframework.util.StopWatch; + +/** + * Utility method that extends Spring Framework StopWatch + * It is a MONOTONIC time stopwatch. + * It is a replacement for any measurements with a wall-clock like System.currentTimeMillis() + * It is not affected by leap second, day-light saving and wall-clock adjustments by manual or network time synchronization + * The main features is a single call for common use cases: + * - create and start: TbStopWatch sw = TbStopWatch.startNew() + * - stop and get: sw.stopAndGetTotalTimeMillis() or sw.stopAndGetLastTaskTimeMillis() + * */ +public class TbStopWatch extends StopWatch { + + public static TbStopWatch startNew(){ + TbStopWatch stopWatch = new TbStopWatch(); + stopWatch.start(); + return stopWatch; + } + + public long stopAndGetTotalTimeMillis(){ + stop(); + return getTotalTimeMillis(); + } + + public long stopAndGetLastTaskTimeMillis(){ + stop(); + return getLastTaskTimeMillis(); + } + +} From f4c98595cb973f1c74f76c647d7bf4c4334851e9 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 11:07:59 +0300 Subject: [PATCH 057/151] generator node: comment added on direct executor --- .../org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index 8fa4b97063..ab9c68e41f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -152,7 +152,7 @@ public class TbMsgGeneratorNode implements TbNode { ctx.logJsEvalResponse(); prevMsg = ctx.newMsg(ServiceQueue.MAIN, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData()); return Futures.immediateFuture(prevMsg); - }, MoreExecutors.directExecutor()); + }, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool } return Futures.immediateFuture(prevMsg); From 585f473bdaa9477fd25616982dcc34ba80d50ed9 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 5 May 2021 15:00:00 +0300 Subject: [PATCH 058/151] exception message added onFailure for GeneratorNode --- .../org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index ab9c68e41f..cb9e25d34b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -117,7 +117,7 @@ public class TbMsgGeneratorNode implements TbNode { } }, t -> { - log.warn("onMsg onFailure callback, took {}ms, config {}, msg {}", sw.stopAndGetTotalTimeMillis(), config, msg); + log.warn("onMsg onFailure callback, took {}ms, config {}, msg {}, exception {}", sw.stopAndGetTotalTimeMillis(), config, msg, t); if (initialized.get() && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { ctx.tellFailure(msg, t); scheduleTickMsg(ctx); From a04eac6015bc98e626f446cfa9b6c4be802df626 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 27 Apr 2021 18:26:52 +0300 Subject: [PATCH 059/151] added logs and timing metrics for DefaultTbQueueRequestTemplate, TbKafkaConsumerTemplate (todo revert or refactor) --- .../queue/common/DefaultTbQueueRequestTemplate.java | 8 ++++---- .../server/queue/kafka/TbKafkaConsumerTemplate.java | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index b171f2d8d8..e07be52491 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -92,10 +92,10 @@ public class DefaultTbQueueRequestTemplate responses = responseTemplate.poll(pollInterval); - if (responses.size() > 0) { - log.trace("Polling responses completed, consumer records count [{}]", responses.size()); - } + final int pendingRequestsCount = pendingRequests.size(); + log.trace("Starting template pool topic {}, for pendingRequests {}", responseTemplate.getTopic(), pendingRequestsCount); + List responses = responseTemplate.poll(pollInterval); //poll js responses + log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); responses.forEach(response -> { byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); UUID requestId; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java index fe4300a421..816a732d41 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java @@ -21,6 +21,7 @@ import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.springframework.util.StopWatch; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueMsg; import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate; @@ -82,7 +83,16 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue @Override protected List> doPoll(long durationInMillis) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + + log.trace("poll topic {} maxDuration {}", getTopic(), durationInMillis); + ConsumerRecords records = consumer.poll(Duration.ofMillis(durationInMillis)); + + stopWatch.stop(); + log.trace("poll topic {} took {}ms", getTopic(), stopWatch.getTotalTimeMillis()); + if (records.isEmpty()) { return Collections.emptyList(); } else { From 67c9025a0625270f02626421d5a47d6c6c79ee3c Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 5 May 2021 18:01:52 +0300 Subject: [PATCH 060/151] improved logs for timeouts for DefaultTbQueueRequestTemplate --- .../server/queue/common/DefaultTbQueueRequestTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index e07be52491..084b43f4b6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -121,7 +121,7 @@ public class DefaultTbQueueRequestTemplate staleRequest = pendingRequests.remove(key); if (staleRequest != null) { - log.trace("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs); + log.info("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs); staleRequest.future.setException(new TimeoutException()); } } @@ -129,7 +129,7 @@ public class DefaultTbQueueRequestTemplate Date: Wed, 5 May 2021 20:10:51 +0300 Subject: [PATCH 061/151] refactored for tests DefaultTbQueueRequestTemplate class --- .../common/DefaultTbQueueRequestTemplate.java | 112 ++++++++++-------- 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 084b43f4b6..2c779d5d5a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -85,59 +85,75 @@ public class DefaultTbQueueRequestTemplate { - long nextCleanupMs = 0L; - while (!stopped) { - try { - final int pendingRequestsCount = pendingRequests.size(); - log.trace("Starting template pool topic {}, for pendingRequests {}", responseTemplate.getTopic(), pendingRequestsCount); - List responses = responseTemplate.poll(pollInterval); //poll js responses - log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); - responses.forEach(response -> { - byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); - UUID requestId; - if (requestIdHeader == null) { - log.error("[{}] Missing requestId in header and body", response); - } else { - requestId = bytesToUuid(requestIdHeader); - log.trace("[{}] Response received: {}", requestId, response); - ResponseMetaData expectedResponse = pendingRequests.remove(requestId); - if (expectedResponse == null) { - log.trace("[{}] Invalid or stale request", requestId); - } else { - expectedResponse.future.set(response); + executor.submit(this::fetchAndProcessResponses); + } + + void fetchAndProcessResponses() { + long nextCleanupMs = 0L; + while (!stopped) { + try { + final int pendingRequestsCount = pendingRequests.size(); + log.trace("Starting template pool topic {}, for pendingRequests {}", responseTemplate.getTopic(), pendingRequestsCount); + List responses = doPoll(); //poll js responses + log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); + responses.forEach(this::processResponse); + responseTemplate.commit(); + tickTs = System.currentTimeMillis(); + tickSize = pendingRequests.size(); + if (nextCleanupMs < tickTs) { + //cleanup; + pendingRequests.forEach((key, value) -> { + if (value.expTime < tickTs) { + ResponseMetaData staleRequest = pendingRequests.remove(key); + if (staleRequest != null) { + setTimeoutException(key, staleRequest); } } }); - responseTemplate.commit(); - tickTs = System.currentTimeMillis(); - tickSize = pendingRequests.size(); - if (nextCleanupMs < tickTs) { - //cleanup; - pendingRequests.forEach((key, value) -> { - if (value.expTime < tickTs) { - ResponseMetaData staleRequest = pendingRequests.remove(key); - if (staleRequest != null) { - log.info("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs); - staleRequest.future.setException(new TimeoutException()); - } - } - }); - nextCleanupMs = tickTs + maxRequestTimeout; - } - } catch (Throwable e) { - log.warn("Failed to obtain responses from queue. Going to sleep " + pollInterval + "ms", e); - try { - Thread.sleep(pollInterval); - } catch (InterruptedException e2) { - log.trace("Failed to wait until the server has capacity to handle new responses", e2); - } + nextCleanupMs = tickTs + maxRequestTimeout; } + } catch (Throwable e) { + log.warn("Failed to obtain responses from queue. Going to sleep " + pollInterval + "ms", e); + sleep(); } - }); + } + } + + List doPoll() { + return responseTemplate.poll(pollInterval); + } + + void sleep() { + try { + Thread.sleep(pollInterval); + } catch (InterruptedException e2) { + log.trace("Failed to wait until the server has capacity to handle new responses", e2); + } + } + + void setTimeoutException(UUID key, ResponseMetaData staleRequest) { + log.info("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs); + staleRequest.future.setException(new TimeoutException()); + } + + void processResponse(Response response) { + byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER); + UUID requestId; + if (requestIdHeader == null) { + log.error("[{}] Missing requestId in header and body", response); + } else { + requestId = bytesToUuid(requestIdHeader); + log.trace("[{}] Response received: {}", requestId, response); + ResponseMetaData expectedResponse = pendingRequests.remove(requestId); + if (expectedResponse == null) { + log.warn("[{}] Invalid or stale request, response: {}", requestId, response); + } else { + expectedResponse.future.set(response); + } + } } @Override @@ -174,7 +190,7 @@ public class DefaultTbQueueRequestTemplate future = SettableFuture.create(); ResponseMetaData responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future); pendingRequests.putIfAbsent(requestId, responseMetaData); - log.trace("[{}] Sending request, key [{}], expTime [{}]", requestId, request.getKey(), responseMetaData.expTime); + log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, request); if (messagesStats != null) { messagesStats.incrementTotal(); } @@ -184,7 +200,7 @@ public class DefaultTbQueueRequestTemplate Date: Thu, 6 May 2021 11:48:42 +0300 Subject: [PATCH 062/151] test: added init stop test for DefaultTbQueueRequestTemplate --- .../common/DefaultTbQueueRequestTemplate.java | 22 ++-- .../DefaultTbQueueRequestTemplateTest.java | 113 ++++++++++++++++++ 2 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 2c779d5d5a..a51e143e26 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -31,6 +31,7 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.common.stats.MessagesStats; +import javax.annotation.Nullable; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -47,14 +48,14 @@ public class DefaultTbQueueRequestTemplate requestTemplate; private final TbQueueConsumer responseTemplate; private final ConcurrentMap> pendingRequests; - private final boolean internalExecutor; + final boolean internalExecutor; private final ExecutorService executor; private final long maxRequestTimeout; private final long maxPendingRequests; private final long pollInterval; - private volatile long tickTs = 0L; - private volatile long tickSize = 0L; - private volatile boolean stopped = false; + volatile long tickTs = 0L; + volatile long tickSize = 0L; + volatile boolean stopped = false; private MessagesStats messagesStats; @@ -65,7 +66,7 @@ public class DefaultTbQueueRequestTemplate requestTemplate; + @Mock + TbQueueConsumer responseTemplate; + + long maxRequestTimeout = 20; + long maxPendingRequests = 10000; + long pollInterval = 25; + String topic = "js-responses-tb-node-0"; + + DefaultTbQueueRequestTemplate inst; + + @Before + public void setUp() throws Exception { + willReturn(topic).given(responseTemplate).getTopic(); + inst = new DefaultTbQueueRequestTemplate( + queueAdmin, requestTemplate, responseTemplate, + maxRequestTimeout, maxPendingRequests, pollInterval, executor); + + } + + @Test + public void givenExternalExecutor_whenStop_thenDoNotShutdownExecutor() { + inst = Mockito.spy(inst); + willDoNothing().given(inst).fetchAndProcessResponses(); + Assert.assertFalse(inst.stopped); + + inst.init(); + Assert.assertNotEquals(inst.tickTs, 0); + Assert.assertFalse(inst.internalExecutor); + verify(queueAdmin, times(1)).createTopicIfNotExists(topic); + verify(requestTemplate, times(1)).init(); + verify(responseTemplate, times(1)).subscribe(); + verify(executor, times(1)).submit(any(Runnable.class)); + + inst.stop(); + Assert.assertTrue(inst.stopped); + verify(responseTemplate, times(1)).unsubscribe(); + verify(requestTemplate, times(1)).stop(); + verify(executor, never()).shutdownNow(); + } + + @Test + public void fetchAndProcessResponses() { + inst.init(); + + inst.stop(); + } +} \ No newline at end of file From 872717828cc0e9338f6d1e986abcb67a56e0d06f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 6 May 2021 14:54:27 +0300 Subject: [PATCH 063/151] test: refactored and added mainLoop test DefaultTbQueueRequestTemplate --- .../DefaultTbQueueRequestTemplateTest.java | 76 ++++++++++++++----- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index 769b66fcd4..413b90c5b9 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -31,27 +31,27 @@ package org.thingsboard.server.queue.common; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.BDDMockito; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.internal.util.reflection.Whitebox; import org.mockito.runners.MockitoJUnitRunner; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; import org.thingsboard.server.queue.TbQueueProducer; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.*; +import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Matchers.any; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -61,16 +61,17 @@ public class DefaultTbQueueRequestTemplateTest { @Mock TbQueueAdmin queueAdmin; @Mock - ExecutorService executor; - @Mock TbQueueProducer requestTemplate; @Mock TbQueueConsumer responseTemplate; + @Mock + ExecutorService executorMock; + ExecutorService executor; + String topic = "js-responses-tb-node-0"; long maxRequestTimeout = 20; long maxPendingRequests = 10000; long pollInterval = 25; - String topic = "js-responses-tb-node-0"; DefaultTbQueueRequestTemplate inst; @@ -79,35 +80,70 @@ public class DefaultTbQueueRequestTemplateTest { willReturn(topic).given(responseTemplate).getTopic(); inst = new DefaultTbQueueRequestTemplate( queueAdmin, requestTemplate, responseTemplate, - maxRequestTimeout, maxPendingRequests, pollInterval, executor); + maxRequestTimeout, maxPendingRequests, pollInterval, executorMock); + + } + @After + public void tearDown() throws Exception { + if (executor != null) { + executor.shutdownNow(); + } } @Test - public void givenExternalExecutor_whenStop_thenDoNotShutdownExecutor() { - inst = Mockito.spy(inst); - willDoNothing().given(inst).fetchAndProcessResponses(); - Assert.assertFalse(inst.stopped); + public void givenInstance_whenVerifyInitialParameters_thenOK() { + assertEquals(maxPendingRequests, inst.maxPendingRequests); + assertEquals(maxRequestTimeout, inst.maxRequestTimeout); + assertEquals(pollInterval, inst.pollInterval); + assertEquals(executorMock, inst.executor); + assertFalse(inst.stopped); + assertFalse(inst.internalExecutor); + } + + @Test + public void givenExternalExecutor_whenInitStop_thenOK() { + inst = spy(inst); + willDoNothing().given(inst).mainLoop(); inst.init(); - Assert.assertNotEquals(inst.tickTs, 0); - Assert.assertFalse(inst.internalExecutor); + assertNotEquals(0, inst.tickTs); + assertEquals(0, inst.nextCleanupMs); verify(queueAdmin, times(1)).createTopicIfNotExists(topic); verify(requestTemplate, times(1)).init(); verify(responseTemplate, times(1)).subscribe(); - verify(executor, times(1)).submit(any(Runnable.class)); + verify(executorMock, times(1)).submit(any(Runnable.class)); inst.stop(); - Assert.assertTrue(inst.stopped); + assertTrue(inst.stopped); verify(responseTemplate, times(1)).unsubscribe(); verify(requestTemplate, times(1)).stop(); - verify(executor, never()).shutdownNow(); + verify(executorMock, never()).shutdownNow(); } @Test - public void fetchAndProcessResponses() { - inst.init(); + public void givenMainLoop_whenLoopFewTimes_thenVerifyInvocationCount() throws InterruptedException { + inst = spy(inst); + executor = inst.createExecutor(); + CountDownLatch latch = new CountDownLatch(5); + willDoNothing().given(inst).sleep(); + willAnswer(invocation -> { + if (latch.getCount() == 1) { + inst.stop(); //stop the loop in natural way + } + if (latch.getCount() == 3 || latch.getCount() == 4) { + latch.countDown(); + throw new RuntimeException("test catch block"); + } + latch.countDown(); + return null; + }).given(inst).fetchAndProcessResponses(); - inst.stop(); + executor.submit(inst::mainLoop); + latch.await(10, TimeUnit.SECONDS); + + verify(inst, times(5)).fetchAndProcessResponses(); + verify(inst, times(2)).sleep(); } + } \ No newline at end of file From d5fffa5002528db63302749b354aff8cd488ce5d Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 6 May 2021 15:50:07 +0300 Subject: [PATCH 064/151] test: added failed test case with overflow maxPendingRequests for DefaultTbQueueRequestTemplate.send(). another refactoring for easy mocking. --- .../DefaultTbQueueRequestTemplateTest.java | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index 413b90c5b9..ef9d0835d2 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -39,17 +39,22 @@ import org.mockito.runners.MockitoJUnitRunner; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; +import org.thingsboard.server.queue.TbQueueMsgHeaders; import org.thingsboard.server.queue.TbQueueProducer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -70,7 +75,7 @@ public class DefaultTbQueueRequestTemplateTest { ExecutorService executor; String topic = "js-responses-tb-node-0"; long maxRequestTimeout = 20; - long maxPendingRequests = 10000; + long maxPendingRequests = 32; long pollInterval = 25; DefaultTbQueueRequestTemplate inst; @@ -78,9 +83,9 @@ public class DefaultTbQueueRequestTemplateTest { @Before public void setUp() throws Exception { willReturn(topic).given(responseTemplate).getTopic(); - inst = new DefaultTbQueueRequestTemplate( + inst = spy(new DefaultTbQueueRequestTemplate( queueAdmin, requestTemplate, responseTemplate, - maxRequestTimeout, maxPendingRequests, pollInterval, executorMock); + maxRequestTimeout, maxPendingRequests, pollInterval, executorMock)); } @@ -103,7 +108,6 @@ public class DefaultTbQueueRequestTemplateTest { @Test public void givenExternalExecutor_whenInitStop_thenOK() { - inst = spy(inst); willDoNothing().given(inst).mainLoop(); inst.init(); @@ -123,7 +127,6 @@ public class DefaultTbQueueRequestTemplateTest { @Test public void givenMainLoop_whenLoopFewTimes_thenVerifyInvocationCount() throws InterruptedException { - inst = spy(inst); executor = inst.createExecutor(); CountDownLatch latch = new CountDownLatch(5); willDoNothing().given(inst).sleep(); @@ -146,4 +149,42 @@ public class DefaultTbQueueRequestTemplateTest { verify(inst, times(2)).sleep(); } + @Test + public void givenMessages_whenSend_thenOK() { + willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any()); + inst.init(); + int msgCount = 10; + for (int i = 0; i < msgCount; i++) { + inst.send(getRequestMsgMock()); + } + assertEquals(msgCount, inst.pendingRequests.size()); + verify(inst, times(msgCount)).sendToRequestTemplate(any(), any(), any(), any()); + } + + @Test + public void givenMessagesOverMaxPendingRequests_whenSend_thenImmediateFailedFutureForTheOfRequests() { + willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any()); + inst.init(); + assertEquals(0, inst.tickSize); + int msgOverflowCount = 10; + for (int i = 0; i < inst.maxPendingRequests; i++) { + assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only + } + for (int i = 0; i < msgOverflowCount; i++) { + assertTrue("max pending requests overflow", inst.send(getRequestMsgMock()).isDone()); //overflow, immediate failed future + } + assertEquals(inst.maxPendingRequests, inst.pendingRequests.size()); + verify(inst, times((int) inst.maxPendingRequests)).sendToRequestTemplate(any(), any(), any(), any()); + } + + @Test + public void givenNothing_whenFetchAndProcessResponsesWithTimeout_thenFail() { + + } + + TbQueueMsg getRequestMsgMock() { + TbQueueMsg requestMsg = mock(TbQueueMsg.class); + willReturn(mock(TbQueueMsgHeaders.class)).given(requestMsg).getHeaders(); + return requestMsg; + } } \ No newline at end of file From 9666986156a0005f2d934646d625b29c02aa091f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 10:04:41 +0300 Subject: [PATCH 065/151] test: added failed test for FetchAndProcessResponses when request removed as staled too early DefaultTbQueueRequestTemplate --- .../common/DefaultTbQueueRequestTemplate.java | 131 ++++++++++++------ .../DefaultTbQueueRequestTemplateTest.java | 60 ++++++-- 2 files changed, 138 insertions(+), 53 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index a51e143e26..bfda553538 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import lombok.Builder; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -47,15 +48,16 @@ public class DefaultTbQueueRequestTemplate requestTemplate; private final TbQueueConsumer responseTemplate; - private final ConcurrentMap> pendingRequests; + final ConcurrentMap> pendingRequests; final boolean internalExecutor; - private final ExecutorService executor; - private final long maxRequestTimeout; - private final long maxPendingRequests; - private final long pollInterval; + final ExecutorService executor; + final long maxRequestTimeout; + final long maxPendingRequests; + final long pollInterval; volatile long tickTs = 0L; volatile long tickSize = 0L; volatile boolean stopped = false; + long nextCleanupMs = 0L; private MessagesStats messagesStats; @@ -75,44 +77,26 @@ public class DefaultTbQueueRequestTemplate responses = doPoll(); //poll js responses - log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); - responses.forEach(this::processResponse); - responseTemplate.commit(); - tickTs = System.currentTimeMillis(); - tickSize = pendingRequests.size(); - if (nextCleanupMs < tickTs) { - //cleanup; - pendingRequests.forEach((key, value) -> { - if (value.expTime < tickTs) { - ResponseMetaData staleRequest = pendingRequests.remove(key); - if (staleRequest != null) { - setTimeoutException(key, staleRequest); - } - } - }); - nextCleanupMs = tickTs + maxRequestTimeout; - } + fetchAndProcessResponses(); } catch (Throwable e) { log.warn("Failed to obtain responses from queue. Going to sleep " + pollInterval + "ms", e); sleep(); @@ -120,6 +104,36 @@ public class DefaultTbQueueRequestTemplate responses = doPoll(); //poll js responses + //if (responses.size() > 0) { + log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); + //} + responses.forEach(this::processResponse); //this can take a long time + responseTemplate.commit(); + tickTs = getCurrentTime(); + tickSize = pendingRequests.size(); + if (nextCleanupMs < tickTs) { + //cleanup; + pendingRequests.forEach((key, value) -> { + if (value.expTime < tickTs) { + ResponseMetaData staleRequest = pendingRequests.remove(key); + if (staleRequest != null) { + setTimeoutException(key, staleRequest, tickTs); + } + } + }); + setupNextCleanup(); + } + } + + void setupNextCleanup() { + nextCleanupMs = tickTs + maxRequestTimeout; + log.info("setupNextCleanup {}", nextCleanupMs); + } + List doPoll() { return responseTemplate.poll(pollInterval); } @@ -132,8 +146,13 @@ public class DefaultTbQueueRequestTemplate staleRequest) { - log.info("[{}] Request timeout detected, expTime [{}], tickTs [{}]", key, staleRequest.expTime, tickTs); + void setTimeoutException(UUID key, ResponseMetaData staleRequest, long tickTs) { + if (tickTs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) { + log.info("Request timeout detected, tickTs [{}], {}, key [{}]", tickTs, staleRequest, key); + } else { + log.error("Request timeout detected, tickTs [{}], {}, key [{}]", tickTs, staleRequest, key); + } + staleRequest.future.setException(new TimeoutException()); } @@ -144,10 +163,10 @@ public class DefaultTbQueueRequestTemplate expectedResponse = pendingRequests.remove(requestId); if (expectedResponse == null) { - log.warn("[{}] Invalid or stale request, response: {}", requestId, response); + log.warn("[{}] Invalid or stale request, response: {}", requestId, String.valueOf(response).replace("\n", " ")); } else { expectedResponse.future.set(response); } @@ -184,11 +203,22 @@ public class DefaultTbQueueRequestTemplate future = SettableFuture.create(); - ResponseMetaData responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future); + ResponseMetaData responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future, currentTime, maxRequestTimeout); + log.info("pending {}", responseMetaData); pendingRequests.putIfAbsent(requestId, responseMetaData); - log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, request); + sendToRequestTemplate(request, requestId, future, responseMetaData); + return future; + } + + long getCurrentTime() { + return System.currentTimeMillis(); + } + + void sendToRequestTemplate(Request request, UUID requestId, SettableFuture future, ResponseMetaData responseMetaData) { + log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, String.valueOf(request).replace("\n", " ")); if (messagesStats != null) { messagesStats.incrementTotal(); } @@ -198,7 +228,7 @@ public class DefaultTbQueueRequestTemplate { + @Getter + static class ResponseMetaData { + private final long submitTime; + private final long timeout; private final long expTime; private final SettableFuture future; - ResponseMetaData(long ts, SettableFuture future) { + ResponseMetaData(long ts, SettableFuture future, long submitTime, long timeout) { + this.submitTime = submitTime; + this.timeout = timeout; this.expTime = ts; this.future = future; } + + @Override + public String toString() { + return "ResponseMetaData{" + + "submitTime=" + submitTime + + ", calculatedExpTime=" + (submitTime + timeout) + + ", expTime=" + expTime + + ", deltaMs=" + (expTime - submitTime) + + ", future=" + future + + '}'; + } } } diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index ef9d0835d2..715b4eab21 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -29,23 +29,29 @@ * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. */ package org.thingsboard.server.queue.common; - +import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; -import org.thingsboard.server.queue.TbQueueMsgHeaders; import org.thingsboard.server.queue.TbQueueProducer; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -54,12 +60,17 @@ import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.hamcrest.MatcherAssert.assertThat; + +@Slf4j @RunWith(MockitoJUnitRunner.class) public class DefaultTbQueueRequestTemplateTest { @@ -74,9 +85,9 @@ public class DefaultTbQueueRequestTemplateTest { ExecutorService executor; String topic = "js-responses-tb-node-0"; - long maxRequestTimeout = 20; + long maxRequestTimeout = 10; long maxPendingRequests = 32; - long pollInterval = 25; + long pollInterval = 5; DefaultTbQueueRequestTemplate inst; @@ -171,20 +182,49 @@ public class DefaultTbQueueRequestTemplateTest { assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only } for (int i = 0; i < msgOverflowCount; i++) { - assertTrue("max pending requests overflow", inst.send(getRequestMsgMock()).isDone()); //overflow, immediate failed future + assertFalse("max pending requests overflow", inst.send(getRequestMsgMock()).isDone()); //overflow, immediate failed future } - assertEquals(inst.maxPendingRequests, inst.pendingRequests.size()); + assertThat(inst.pendingRequests.size(), equalTo(inst.maxPendingRequests)); verify(inst, times((int) inst.maxPendingRequests)).sendToRequestTemplate(any(), any(), any(), any()); } @Test - public void givenNothing_whenFetchAndProcessResponsesWithTimeout_thenFail() { + public void givenNothing_whenSendAndFetchAndProcessResponsesWithTimeout_thenFail() { + //given + AtomicLong currentTime = new AtomicLong(); + willAnswer(x -> { + log.info("currentTime={}", currentTime.get()); + return currentTime.get(); + }).given(inst).getCurrentTime(); + inst.init(); + inst.setupNextCleanup(); + willReturn(Collections.emptyList()).given(inst).doPoll(); + willDoNothing().given(inst).processResponse(any()); + + //when + for (int i = 0; i <= inst.maxRequestTimeout*2; i++) { + currentTime.incrementAndGet(); + assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only + if (i % (inst.maxRequestTimeout * 3 / 2) == 0) { + inst.fetchAndProcessResponses(); + } + } + + //then + ArgumentCaptor argumentCaptorResp = ArgumentCaptor.forClass(DefaultTbQueueRequestTemplate.ResponseMetaData.class); + ArgumentCaptor argumentCaptorUUID = ArgumentCaptor.forClass(UUID.class); + ArgumentCaptor argumentCaptorLong = ArgumentCaptor.forClass(Long.class); + verify(inst, atLeastOnce()).setTimeoutException(argumentCaptorUUID.capture(), argumentCaptorResp.capture(), argumentCaptorLong.capture()); + + List responseMetaDataList = argumentCaptorResp.getAllValues(); + List tickTsList = argumentCaptorLong.getAllValues(); + for (int i = 0; i < responseMetaDataList.size(); i++) { + assertThat("tickTs >= calculatedExpTime", tickTsList.get(i), greaterThanOrEqualTo(responseMetaDataList.get(i).getSubmitTime() + responseMetaDataList.get(i).getTimeout())); + } } TbQueueMsg getRequestMsgMock() { - TbQueueMsg requestMsg = mock(TbQueueMsg.class); - willReturn(mock(TbQueueMsgHeaders.class)).given(requestMsg).getHeaders(); - return requestMsg; + return mock(TbQueueMsg.class, RETURNS_DEEP_STUBS); } } \ No newline at end of file From 28235732c6140dfc9a5cb81d4c2e39b8ac217532 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 12:07:17 +0300 Subject: [PATCH 066/151] test: fixed class DefaultTbQueueRequestTemplate --- .../common/DefaultTbQueueRequestTemplate.java | 90 +++++++++++-------- .../DefaultTbQueueRequestTemplateTest.java | 37 ++++---- 2 files changed, 73 insertions(+), 54 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index bfda553538..d5179385e6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -36,10 +36,12 @@ import javax.annotation.Nullable; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; @Slf4j public class DefaultTbQueueRequestTemplate extends AbstractTbQueueTemplate @@ -48,16 +50,15 @@ public class DefaultTbQueueRequestTemplate requestTemplate; private final TbQueueConsumer responseTemplate; - final ConcurrentMap> pendingRequests; + final ConcurrentHashMap> pendingRequests = new ConcurrentHashMap<>(); final boolean internalExecutor; final ExecutorService executor; - final long maxRequestTimeout; + final long maxRequestTimeoutNs; final long maxPendingRequests; final long pollInterval; - volatile long tickTs = 0L; - volatile long tickSize = 0L; volatile boolean stopped = false; - long nextCleanupMs = 0L; + long nextCleanupNs = 0L; + private final Lock cleanerLock = new ReentrantLock(); private MessagesStats messagesStats; @@ -72,8 +73,7 @@ public class DefaultTbQueueRequestTemplate(); - this.maxRequestTimeout = maxRequestTimeout; + this.maxRequestTimeoutNs = TimeUnit.MILLISECONDS.toNanos(maxRequestTimeout); this.maxPendingRequests = maxPendingRequests; this.pollInterval = pollInterval; this.internalExecutor = (executor == null); @@ -88,7 +88,6 @@ public class DefaultTbQueueRequestTemplate responses = doPoll(); //poll js responses //if (responses.size() > 0) { @@ -113,25 +112,36 @@ public class DefaultTbQueueRequestTemplate { - if (value.expTime < tickTs) { - ResponseMetaData staleRequest = pendingRequests.remove(key); - if (staleRequest != null) { - setTimeoutException(key, staleRequest, tickTs); + tryCleanStaleRequests(); + } + + private boolean tryCleanStaleRequests() { + if (!cleanerLock.tryLock()) { + return false; + } + try { + log.trace("tryCleanStaleRequest..."); + final long currentNs = getCurrentClockNs(); + if (nextCleanupNs < currentNs) { + pendingRequests.forEach((key, value) -> { + if (value.expTime < currentNs) { + ResponseMetaData staleRequest = pendingRequests.remove(key); + if (staleRequest != null) { + setTimeoutException(key, staleRequest, currentNs); + } } - } - }); - setupNextCleanup(); + }); + setupNextCleanup(); + } + } finally { + cleanerLock.unlock(); } + return true; } void setupNextCleanup() { - nextCleanupMs = tickTs + maxRequestTimeout; - log.info("setupNextCleanup {}", nextCleanupMs); + nextCleanupNs = getCurrentClockNs() + maxRequestTimeoutNs; + log.info("setupNextCleanup {}", nextCleanupNs); } List doPoll() { @@ -146,11 +156,11 @@ public class DefaultTbQueueRequestTemplate staleRequest, long tickTs) { - if (tickTs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) { - log.info("Request timeout detected, tickTs [{}], {}, key [{}]", tickTs, staleRequest, key); + void setTimeoutException(UUID key, ResponseMetaData staleRequest, long currentNs) { + if (currentNs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) { + log.info("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } else { - log.error("Request timeout detected, tickTs [{}], {}, key [{}]", tickTs, staleRequest, key); + log.error("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } staleRequest.future.setException(new TimeoutException()); @@ -197,23 +207,31 @@ public class DefaultTbQueueRequestTemplate send(Request request) { - if (tickSize > maxPendingRequests) { + if (pendingRequests.mappingCount() >= maxPendingRequests) { + log.warn("Pending request map is full [{}]! Consider to increase maxPendingRequests or increase processing performance", maxPendingRequests); return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!")); } UUID requestId = UUID.randomUUID(); request.getHeaders().put(REQUEST_ID_HEADER, uuidToBytes(requestId)); request.getHeaders().put(RESPONSE_TOPIC_HEADER, stringToBytes(responseTemplate.getTopic())); - long currentTime = getCurrentTime(); - request.getHeaders().put(REQUEST_TIME, longToBytes(currentTime)); + request.getHeaders().put(REQUEST_TIME, longToBytes(getCurrentTimeMs())); + long currentClockNs = getCurrentClockNs(); SettableFuture future = SettableFuture.create(); - ResponseMetaData responseMetaData = new ResponseMetaData<>(tickTs + maxRequestTimeout, future, currentTime, maxRequestTimeout); - log.info("pending {}", responseMetaData); - pendingRequests.putIfAbsent(requestId, responseMetaData); + ResponseMetaData responseMetaData = new ResponseMetaData<>(currentClockNs + maxRequestTimeoutNs, future, currentClockNs, maxRequestTimeoutNs); + log.info("pending {}", responseMetaData); //TODO trace + if (pendingRequests.putIfAbsent(requestId, responseMetaData) != null) { + log.warn("Pending request already exists [{}]!", maxPendingRequests); + return Futures.immediateFailedFuture(new RuntimeException("Pending request already exists !" + requestId)); + } sendToRequestTemplate(request, requestId, future, responseMetaData); return future; } - long getCurrentTime() { + long getCurrentClockNs() { + return System.nanoTime(); //MONOTONIC clock instead wall clock + } + + long getCurrentTimeMs() { //Wall clock to send Ts to the an external service return System.currentTimeMillis(); } @@ -261,8 +279,8 @@ public class DefaultTbQueueRequestTemplate { log.info("currentTime={}", currentTime.get()); return currentTime.get(); - }).given(inst).getCurrentTime(); + }).given(inst).getCurrentClockNs(); inst.init(); inst.setupNextCleanup(); willReturn(Collections.emptyList()).given(inst).doPoll(); willDoNothing().given(inst).processResponse(any()); //when - for (int i = 0; i <= inst.maxRequestTimeout*2; i++) { - currentTime.incrementAndGet(); + long stepNs = TimeUnit.MILLISECONDS.toNanos(1); + for (long i = 0; i <= inst.maxRequestTimeoutNs * 2; i = i + stepNs) { + currentTime.addAndGet(stepNs); assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only - if (i % (inst.maxRequestTimeout * 3 / 2) == 0) { + if (i % (inst.maxRequestTimeoutNs * 3 / 2) == 0) { inst.fetchAndProcessResponses(); } } From 928b8f0fd9ca39fe7ebe874c7ea195153779b96d Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 12:26:44 +0300 Subject: [PATCH 067/151] test: refactored for assertThat for DefaultTbQueueRequestTemplateTest --- .../common/DefaultTbQueueRequestTemplate.java | 5 ++--- .../DefaultTbQueueRequestTemplateTest.java | 17 ++++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index d5179385e6..c31dc3a899 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -158,11 +158,10 @@ public class DefaultTbQueueRequestTemplate staleRequest, long currentNs) { if (currentNs >= staleRequest.getSubmitTime() + staleRequest.getTimeout()) { - log.info("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); + log.warn("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } else { log.error("Request timeout detected, currentNs [{}], {}, key [{}]", currentNs, staleRequest, key); } - staleRequest.future.setException(new TimeoutException()); } @@ -218,7 +217,7 @@ public class DefaultTbQueueRequestTemplate future = SettableFuture.create(); ResponseMetaData responseMetaData = new ResponseMetaData<>(currentClockNs + maxRequestTimeoutNs, future, currentClockNs, maxRequestTimeoutNs); - log.info("pending {}", responseMetaData); //TODO trace + log.trace("pending {}", responseMetaData); if (pendingRequests.putIfAbsent(requestId, responseMetaData) != null) { log.warn("Pending request already exists [{}]!", maxPendingRequests); return Futures.immediateFailedFuture(new RuntimeException("Pending request already exists !" + requestId)); diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index 33dfd82989..261ff9c7be 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -87,7 +87,7 @@ public class DefaultTbQueueRequestTemplateTest { ExecutorService executor; String topic = "js-responses-tb-node-0"; long maxRequestTimeout = 10; - long maxPendingRequests = 1000; + long maxPendingRequests = 32; long pollInterval = 5; DefaultTbQueueRequestTemplate inst; @@ -124,14 +124,14 @@ public class DefaultTbQueueRequestTemplateTest { inst.init(); //assertNotEquals(0, inst.tickTs); - assertEquals(0, inst.nextCleanupNs); + assertThat(inst.nextCleanupNs, equalTo(0L)); verify(queueAdmin, times(1)).createTopicIfNotExists(topic); verify(requestTemplate, times(1)).init(); verify(responseTemplate, times(1)).subscribe(); verify(executorMock, times(1)).submit(any(Runnable.class)); inst.stop(); - assertTrue(inst.stopped); + assertThat(inst.stopped, is(true)); verify(responseTemplate, times(1)).unsubscribe(); verify(requestTemplate, times(1)).stop(); verify(executorMock, never()).shutdownNow(); @@ -165,11 +165,11 @@ public class DefaultTbQueueRequestTemplateTest { public void givenMessages_whenSend_thenOK() { willDoNothing().given(inst).sendToRequestTemplate(any(), any(), any(), any()); inst.init(); - int msgCount = 10; + final int msgCount = 10; for (int i = 0; i < msgCount; i++) { inst.send(getRequestMsgMock()); } - assertEquals(msgCount, inst.pendingRequests.mappingCount()); + assertThat(inst.pendingRequests.mappingCount(), equalTo((long) msgCount)); verify(inst, times(msgCount)).sendToRequestTemplate(any(), any(), any(), any()); } @@ -179,10 +179,10 @@ public class DefaultTbQueueRequestTemplateTest { inst.init(); int msgOverflowCount = 10; for (int i = 0; i < inst.maxPendingRequests; i++) { - assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only + assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only } for (int i = 0; i < msgOverflowCount; i++) { - assertTrue("max pending requests overflow", inst.send(getRequestMsgMock()).isDone()); //overflow, immediate failed future + assertThat("max pending requests overflow", inst.send(getRequestMsgMock()).isDone(), is(true)); //overflow, immediate failed future } assertThat(inst.pendingRequests.mappingCount(), equalTo(inst.maxPendingRequests)); verify(inst, times((int) inst.maxPendingRequests)).sendToRequestTemplate(any(), any(), any(), any()); @@ -205,7 +205,7 @@ public class DefaultTbQueueRequestTemplateTest { long stepNs = TimeUnit.MILLISECONDS.toNanos(1); for (long i = 0; i <= inst.maxRequestTimeoutNs * 2; i = i + stepNs) { currentTime.addAndGet(stepNs); - assertFalse(inst.send(getRequestMsgMock()).isDone()); //SettableFuture future - pending only + assertThat(inst.send(getRequestMsgMock()).isDone(), is(false)); //SettableFuture future - pending only if (i % (inst.maxRequestTimeoutNs * 3 / 2) == 0) { inst.fetchAndProcessResponses(); } @@ -222,7 +222,6 @@ public class DefaultTbQueueRequestTemplateTest { for (int i = 0; i < responseMetaDataList.size(); i++) { assertThat("tickTs >= calculatedExpTime", tickTsList.get(i), greaterThanOrEqualTo(responseMetaDataList.get(i).getSubmitTime() + responseMetaDataList.get(i).getTimeout())); } - } TbQueueMsg getRequestMsgMock() { From 1e066f21569aa7824177a6080577ac83d29ef2a7 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 12:41:50 +0300 Subject: [PATCH 068/151] added timeout parameter to the TbQueueRequestTemplate.send( ) --- .../server/queue/TbQueueRequestTemplate.java | 2 ++ .../common/DefaultTbQueueRequestTemplate.java | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java index 5dc89a9c26..192f8e1675 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java @@ -24,6 +24,8 @@ public interface TbQueueRequestTemplate send(Request request); + ListenableFuture send(Request request, long timeoutNs); + void stop(); void setMessagesStats(MessagesStats messagesStats); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index c31dc3a899..5b03e1bc2d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -206,6 +206,11 @@ public class DefaultTbQueueRequestTemplate send(Request request) { + return send(request, this.maxRequestTimeoutNs); + } + + @Override + public ListenableFuture send(Request request, long requestTimeoutNs) { if (pendingRequests.mappingCount() >= maxPendingRequests) { log.warn("Pending request map is full [{}]! Consider to increase maxPendingRequests or increase processing performance", maxPendingRequests); return Futures.immediateFailedFuture(new RuntimeException("Pending request map is full!")); @@ -216,7 +221,7 @@ public class DefaultTbQueueRequestTemplate future = SettableFuture.create(); - ResponseMetaData responseMetaData = new ResponseMetaData<>(currentClockNs + maxRequestTimeoutNs, future, currentClockNs, maxRequestTimeoutNs); + ResponseMetaData responseMetaData = new ResponseMetaData<>(currentClockNs + requestTimeoutNs, future, currentClockNs, requestTimeoutNs); log.trace("pending {}", responseMetaData); if (pendingRequests.putIfAbsent(requestId, responseMetaData) != null) { log.warn("Pending request already exists [{}]!", maxPendingRequests); @@ -226,11 +231,18 @@ public class DefaultTbQueueRequestTemplate Date: Fri, 7 May 2021 14:07:20 +0300 Subject: [PATCH 069/151] sleep(poolInterval) replaced with parkNanos(1) for DefaultTbQueueRequestTemplate --- .../server/actors/service/DefaultActorService.java | 10 +--------- .../queue/common/DefaultTbQueueRequestTemplate.java | 10 ++++------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index 7a28c205bc..6f359c00b0 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -102,15 +102,7 @@ public class DefaultActorService extends TbApplicationEventListener staleRequest, long currentNs) { From 16a55b16e4f7c0f678cd10088b4f8ae94c5d5921 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 15:56:45 +0300 Subject: [PATCH 070/151] js invoke service: logs added when script going to disable due to exception (special log for TimeoutException) --- .../service/script/AbstractJsInvokeService.java | 15 ++++++++++++--- .../script/AbstractNashornJsInvokeService.java | 2 +- .../service/script/RemoteJsInvokeService.java | 7 ++++--- .../common/DefaultTbQueueRequestTemplate.java | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java index 2f0378ae9d..b089f52e76 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java @@ -30,6 +30,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** @@ -84,8 +85,10 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1); return doInvokeFunction(scriptId, functionName, args); } else { - return Futures.immediateFailedFuture( - new RuntimeException("Script invocation is blocked due to maximum error count " + getMaxErrors() + "!")); + String message = "Script invocation is blocked due to maximum error count " + + getMaxErrors() + ", scriptId " + scriptId + "!"; + log.warn(message); + return Futures.immediateFailedFuture(new RuntimeException(message)); } } else { return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!")); @@ -117,7 +120,13 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { protected abstract long getMaxBlacklistDuration(); - protected void onScriptExecutionError(UUID scriptId) { + protected void onScriptExecutionError(UUID scriptId, Throwable t) { + if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { + log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test + disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).get(), + scriptId); + //return; //timeout is not a good reason to disable function + } disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).incrementAndGet(); } diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java index 9985ac60a2..f9fd3db977 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java @@ -160,7 +160,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer return ((Invocable) engine).invokeFunction(functionName, args); } } catch (Exception e) { - onScriptExecutionError(scriptId); + onScriptExecutionError(scriptId, e); throw new ExecutionException(e); } }); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index 334a471973..b8bfeea75b 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java @@ -193,7 +193,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Override public void onFailure(Throwable t) { - onScriptExecutionError(scriptId); + onScriptExecutionError(scriptId, t); if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { queueTimeoutMsgs.incrementAndGet(); } @@ -205,9 +205,10 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { if (invokeResult.getSuccess()) { return invokeResult.getResult(); } else { - onScriptExecutionError(scriptId); + final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails()); + onScriptExecutionError(scriptId, e); log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails()); - throw new RuntimeException(invokeResult.getErrorDetails()); + throw e; } }, callbackExecutor); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index f6a5b33289..7b0e7a14cd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -109,7 +109,7 @@ public class DefaultTbQueueRequestTemplate responses = doPoll(); //poll js responses //if (responses.size() > 0) { - log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); + log.info("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); //TODO reduce verbose after test //} responses.forEach(this::processResponse); //this can take a long time responseTemplate.commit(); From 24eca345768009b674ad827937247bcdd8cba523 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 5 May 2021 08:22:38 +0300 Subject: [PATCH 071/151] new utility class tbStopWatch added to improve performance measurement experience --- common/util/pom.xml | 5 ++ .../thingsboard/common/util/TbStopWatch.java | 62 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java diff --git a/common/util/pom.xml b/common/util/pom.xml index 6f66da5790..128bc478d5 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -36,6 +36,11 @@ + + org.springframework + spring-core + ${spring.version} + com.google.guava guava diff --git a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java new file mode 100644 index 0000000000..b7dbd0052c --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java @@ -0,0 +1,62 @@ +/** + * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * + * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of ThingsBoard, Inc. and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to ThingsBoard, Inc. + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * + * Dissemination of this information or reproduction of this material is strictly forbidden + * unless prior written permission is obtained from COMPANY. + * + * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, + * managers or contractors who have executed Confidentiality and Non-disclosure agreements + * explicitly covering such access. + * + * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes + * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. + * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, + * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT + * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, + * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. + * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION + * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, + * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + */ +package org.thingsboard.common.util; + +import org.springframework.util.StopWatch; + +/** + * Utility method that extends Spring Framework StopWatch + * It is a MONOTONIC time stopwatch. + * It is a replacement for any measurements with a wall-clock like System.currentTimeMillis() + * It is not affected by leap second, day-light saving and wall-clock adjustments by manual or network time synchronization + * The main features is a single call for common use cases: + * - create and start: TbStopWatch sw = TbStopWatch.startNew() + * - stop and get: sw.stopAndGetTotalTimeMillis() or sw.stopAndGetLastTaskTimeMillis() + * */ +public class TbStopWatch extends StopWatch { + + public static TbStopWatch startNew(){ + TbStopWatch stopWatch = new TbStopWatch(); + stopWatch.start(); + return stopWatch; + } + + public long stopAndGetTotalTimeMillis(){ + stop(); + return getTotalTimeMillis(); + } + + public long stopAndGetLastTaskTimeMillis(){ + stop(); + return getLastTaskTimeMillis(); + } + +} From e2aa4be7415790fdf371023fe22a4c25e5fcc605 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 16:26:20 +0300 Subject: [PATCH 072/151] TbStopWatch: methods for nanos added --- .../java/org/thingsboard/common/util/TbStopWatch.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java index b7dbd0052c..4f5d1346a9 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java +++ b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java @@ -54,9 +54,19 @@ public class TbStopWatch extends StopWatch { return getTotalTimeMillis(); } + public long stopAndGetTotalTimeNanos(){ + stop(); + return getLastTaskTimeNanos(); + } + public long stopAndGetLastTaskTimeMillis(){ stop(); return getLastTaskTimeMillis(); } + public long stopAndGetLastTaskTimeNanos(){ + stop(); + return getLastTaskTimeNanos(); + } + } From 9daa43a115bd584a409ea1279ca4ef6b4b0ae5cb Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 16:34:35 +0300 Subject: [PATCH 073/151] queue request template: sleep on exception shortened according to the stopwatch. test adjusted --- .../queue/common/DefaultTbQueueRequestTemplate.java | 12 +++++++----- .../common/DefaultTbQueueRequestTemplateTest.java | 10 +++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 7b0e7a14cd..a385ef17a8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.SettableFuture; import lombok.Builder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.TbStopWatch; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueAdmin; @@ -95,11 +96,13 @@ public class DefaultTbQueueRequestTemplate staleRequest, long currentNs) { diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index 261ff9c7be..e60e4071f3 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -54,13 +54,13 @@ import java.util.concurrent.atomic.AtomicLong; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.lessThan; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.longThat; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -141,7 +141,7 @@ public class DefaultTbQueueRequestTemplateTest { public void givenMainLoop_whenLoopFewTimes_thenVerifyInvocationCount() throws InterruptedException { executor = inst.createExecutor(); CountDownLatch latch = new CountDownLatch(5); - willDoNothing().given(inst).sleep(); + willDoNothing().given(inst).sleep(anyLong()); willAnswer(invocation -> { if (latch.getCount() == 1) { inst.stop(); //stop the loop in natural way @@ -158,7 +158,7 @@ public class DefaultTbQueueRequestTemplateTest { latch.await(10, TimeUnit.SECONDS); verify(inst, times(5)).fetchAndProcessResponses(); - verify(inst, times(2)).sleep(); + verify(inst, times(2)).sleep(longThat(lessThan(TimeUnit.MILLISECONDS.toNanos(inst.pollInterval)))); } @Test From c3aec4d077220aef22f369c6d6ee692205b75a47 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Fri, 7 May 2021 15:59:42 +0300 Subject: [PATCH 074/151] revert initDispatcherExecutor --- .../server/actors/service/DefaultActorService.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index 6f359c00b0..98f739a24b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -102,7 +102,16 @@ public class DefaultActorService extends TbApplicationEventListener Date: Tue, 11 May 2021 08:01:21 +0300 Subject: [PATCH 075/151] log WARN for onScriptExecutionError to prevent silent ban (actually, system going to idle if the script is a part of a rule chain). restored TRACE level on fetchAndProcessResponses --- .../service/script/AbstractJsInvokeService.java | 17 ++++++++++------- .../common/DefaultTbQueueRequestTemplate.java | 6 ++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java index b089f52e76..dc01e0d97e 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java @@ -121,13 +121,16 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { protected abstract long getMaxBlacklistDuration(); protected void onScriptExecutionError(UUID scriptId, Throwable t) { - if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { - log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test - disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).get(), - scriptId); - //return; //timeout is not a good reason to disable function - } - disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).incrementAndGet(); + DisableListInfo disableListInfo = disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()); + log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}", + disableListInfo.get(), scriptId, t.getClass(), t.getCause()); +// if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { +// log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test +// disableListInfo.get(), +// scriptId); +// //return; //timeout is not a good reason to disable function +// } + disableListInfo.incrementAndGet(); } private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index a385ef17a8..abeb739160 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -109,11 +109,9 @@ public class DefaultTbQueueRequestTemplate responses = doPoll(); //poll js responses - //if (responses.size() > 0) { - log.info("Completed template poll topic {}, for pendingRequests [{}], received [{}]", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); //TODO reduce verbose after test - //} + log.trace("Completed template poll topic {}, for pendingRequests [{}], received [{}] responses", responseTemplate.getTopic(), pendingRequestsCount, responses.size()); responses.forEach(this::processResponse); //this can take a long time responseTemplate.commit(); tryCleanStaleRequests(); From d3e4102282b581b055ec34c0fa555711a19ae472 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 May 2021 08:11:15 +0300 Subject: [PATCH 076/151] removed version from pom.xml from common/util for spring-core (version already defined through the root dependency management) --- common/util/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/common/util/pom.xml b/common/util/pom.xml index 128bc478d5..8a0fa1705b 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -39,7 +39,6 @@ org.springframework spring-core - ${spring.version} com.google.guava From da65eaab473d1f89131b71b0710d0c3c53e130cf Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 May 2021 08:20:16 +0300 Subject: [PATCH 077/151] added spring-core for the root dependency management --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index b7d0eb71f6..5f88903559 100755 --- a/pom.xml +++ b/pom.xml @@ -1009,6 +1009,11 @@ + + org.springframework + spring-core + ${spring.version} + org.springframework.boot spring-boot-starter-web From ddfb57db588dc0e1de67dd662af49a50c2ed0cd2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 May 2021 09:30:58 +0300 Subject: [PATCH 078/151] log detailed error onScriptExecutionError for debug --- .../server/service/script/AbstractJsInvokeService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java index dc01e0d97e..8a6aeb399b 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java @@ -123,7 +123,8 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { protected void onScriptExecutionError(UUID scriptId, Throwable t) { DisableListInfo disableListInfo = disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()); log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}", - disableListInfo.get(), scriptId, t.getClass(), t.getCause()); + disableListInfo.get(), scriptId, t, t.getCause()); + log.error("onScriptExecutionError", t); // if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { // log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test // disableListInfo.get(), From 5ada6b5accd763ac35cecc562dfdbb6a83fb6775 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 17 Jun 2021 13:09:23 +0300 Subject: [PATCH 079/151] LwM2M Firmware Update Delivery Method --- .../transport/lwm2m/server/LwM2mTransportUtil.java | 3 +++ .../server/ota/DefaultLwM2MOtaUpdateService.java | 14 +++++++++++++- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 2 ++ .../lwm2m/server/ota/LwM2MOtaUpdateService.java | 2 ++ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 3 +++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index c9248ca264..f310eb6481 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -137,6 +137,9 @@ public class LwM2mTransportUtil { public static final String FW_STATE_ID = "/5/0/3"; // Update Result R public static final String FW_RESULT_ID = "/5/0/5"; + + public static final String FW_DELIVERY_METHOD = "/5/0/9"; + // PkgName R public static final String FW_NAME_ID = "/5/0/6"; // PkgVersion R diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 68d7d76b73..d03a666ed5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; @@ -180,6 +181,12 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { } } + @Override + public void onCurrentFirmwareDeliveryMethodUpdate(LwM2mClient client, Long value) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setDeliveryMethod(value.intValue()); + } + @Override public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion) { @@ -211,7 +218,12 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); - var strategy = fwInfo.getStrategy(); + LwM2MFirmwareUpdateStrategy strategy; + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { + strategy = fwInfo.getStrategy(); + } else { + strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; + } switch (strategy) { case OBJ_5_BINARY: byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 26f7abed4f..78ae7220e2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -41,6 +41,7 @@ public class LwM2MClientOtaInfo { private boolean currentFetchFailure; private String currentName; private String currentVersion; + private Integer deliveryMethod; //TODO: use value from device if applicable; private LwM2MFirmwareUpdateStrategy strategy; @@ -96,4 +97,5 @@ public class LwM2MClientOtaInfo { private static String getPackageId(String name, String version) { return (StringUtils.isNotEmpty(name) ? name : "") + (StringUtils.isNotEmpty(version) ? version : ""); } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index e1bde7df8b..6386e94364 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -34,4 +34,6 @@ public interface LwM2MOtaUpdateService { void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state); void onCurrentFirmwareResultUpdate(LwM2mClient client, Long result); + + void onCurrentFirmwareDeliveryMethodUpdate(LwM2mClient lwM2MClient, Long value); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 97fe273857..61c4b077d9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -109,6 +109,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAIL import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_DELIVERY_METHOD; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; @@ -571,6 +572,8 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, registration))) { otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_DELIVERY_METHOD, registration))) { + otaService.onCurrentFirmwareDeliveryMethodUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } this.updateAttrTelemetry(registration, Collections.singleton(path)); } else { From 56a6e601bcf357d3e1e38358fabd6c30a8cebf21 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 17 Jun 2021 12:44:18 +0300 Subject: [PATCH 080/151] added lwm2m test for firmware --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 4 +- .../lwm2m/NoSecLwM2MIntegrationTest.java | 79 ++++++++++++++++++- .../lwm2m/X509LwM2MIntegrationTest.java | 1 - 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 6db6e24f91..ad156ca3a7 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -103,7 +103,9 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { " }\n" + " },\n" + " \"clientLwM2mSettings\": {\n" + - " \"clientOnlyObserveAfterConnect\": 1\n" + + " \"clientOnlyObserveAfterConnect\": 1,\n" + + " \"fwUpdateStrategy\": 1,\n" + + " \"swUpdateStrategy\": 1\n" + " }\n" + "}"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java index 7d173e61d9..f54a6e3382 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java @@ -17,11 +17,14 @@ package org.thingsboard.server.transport.lwm2m; import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; -import org.jetbrains.annotations.NotNull; import org.junit.Assert; import org.junit.Test; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; @@ -43,6 +46,7 @@ import java.util.List; import static org.eclipse.leshan.client.object.Security.noSec; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @@ -51,7 +55,6 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); private final String ENDPOINT = "deviceAEndpoint"; - @NotNull private Device createDevice() throws Exception { Device device = new Device(); device.setName("Device A"); @@ -74,6 +77,29 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { return device; } + private OtaPackageInfo createFirmware() throws Exception { + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; + + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); + firmwareInfo.setDeviceProfileId(deviceProfile.getId()); + firmwareInfo.setType(FIRMWARE); + firmwareInfo.setTitle("My firmware"); + firmwareInfo.setVersion("v1.0"); + + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); + + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); + + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); + } + + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); + postRequest.file(content); + setJwtToken(postRequest); + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); + } + @Test public void testConnectAndObserveTelemetry() throws Exception { createDeviceProfile(TRANSPORT_CONFIGURATION); @@ -111,4 +137,53 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { client.destroy(); } + @Test + public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception { + createDeviceProfile(TRANSPORT_CONFIGURATION); + + Device device = createDevice(); + + OtaPackageInfo firmware = createFirmware(); + + LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT); + client.init(SECURITY, COAP_CONFIG); + + device.setFirmwareId(firmware.getId()); + + Thread.sleep(1000); + + device = doPost("/api/device", device, Device.class); + + Thread.sleep(1000); + + SingleEntityFilter sef = new SingleEntityFilter(); + sef.setSingleEntity(device.getId()); + LatestValueCmd latestCmd = new LatestValueCmd(); + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "fw_state"))); + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); + + wsClient.send(mapper.writeValueAsString(wrapper)); + wsClient.waitForReply(); + + wsClient.registerWaitForUpdate(); + + String msg = wsClient.waitForUpdate(); + + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + Assert.assertEquals(1, update.getCmdId()); + List eData = update.getUpdate(); + Assert.assertNotNull(eData); + Assert.assertEquals(1, eData.size()); + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("fw_state"); + Assert.assertEquals("FAILED", tsValue.getValue()); + client.destroy(); + } + } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java index 0c13c57441..a5efff968a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java @@ -52,7 +52,6 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { private final String endpoint = "deviceAEndpoint"; private final String serverUri = "coaps://localhost:" + port; - @NotNull private Device createDevice(X509ClientCredentials clientCredentials) throws Exception { Device device = new Device(); device.setName("Device A"); From b4a90ceab97356a4f36e77c9f3cf0048e11e3c22 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 17 Jun 2021 13:02:47 +0300 Subject: [PATCH 081/151] improved lwm2m test for firmware --- .../server/transport/TransportSqlTestSuite.java | 14 +++++++------- .../transport/lwm2m/NoSecLwM2MIntegrationTest.java | 4 ++-- .../server/ota/DefaultLwM2MOtaUpdateService.java | 4 +++- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 5 +++++ 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java index 25df3bee00..d059ea1449 100644 --- a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java @@ -26,13 +26,13 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.transport.*.rpc.sql.*Test", - "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", - "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", - "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", - "org.thingsboard.server.transport.*.attributes.request.sql.*Test", - "org.thingsboard.server.transport.*.claim.sql.*Test", - "org.thingsboard.server.transport.*.provision.sql.*Test", +// "org.thingsboard.server.transport.*.rpc.sql.*Test", +// "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", +// "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", +// "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", +// "org.thingsboard.server.transport.*.attributes.request.sql.*Test", +// "org.thingsboard.server.transport.*.claim.sql.*Test", +// "org.thingsboard.server.transport.*.provision.sql.*Test", "org.thingsboard.server.transport.lwm2m.*Test" }) public class TransportSqlTestSuite { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java index f54a6e3382..e94f432f64 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java @@ -148,10 +148,10 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT); client.init(SECURITY, COAP_CONFIG); - device.setFirmwareId(firmware.getId()); - Thread.sleep(1000); + device.setFirmwareId(firmware.getId()); + device = doPost("/api/device", device, Device.class); Thread.sleep(1000); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index d03a666ed5..3f86131320 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -36,8 +36,8 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; @@ -200,6 +200,8 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { } else { startFirmwareUpdateUsingBinary(client, fwInfo); } + } else if (fwInfo.isUpdateFailed()) { + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 78ae7220e2..50b44f8d06 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -80,6 +80,11 @@ public class LwM2MClientOtaInfo { } } + public boolean isUpdateFailed() { + return (StringUtils.isNotEmpty(targetName) && StringUtils.isNotEmpty(targetVersion) && + (StringUtils.isEmpty(currentName) && StringUtils.isEmpty(currentVersion))); + } + public void setUpdateResult(UpdateResultFw updateResult) { this.updateResult = updateResult; switch (updateResult) { From 1d33c09127165907def60b09bca41a11b60d8d85 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 11 May 2021 16:22:10 +0300 Subject: [PATCH 082/151] JsInvokeService - log js body on script failure to investigate easy --- .../server/service/script/AbstractJsInvokeService.java | 7 +++---- .../service/script/AbstractNashornJsInvokeService.java | 2 +- .../server/service/script/RemoteJsInvokeService.java | 10 +++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java index 8a6aeb399b..cae4a8e05c 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractJsInvokeService.java @@ -120,11 +120,10 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { protected abstract long getMaxBlacklistDuration(); - protected void onScriptExecutionError(UUID scriptId, Throwable t) { + protected void onScriptExecutionError(UUID scriptId, Throwable t, String scriptBody) { DisableListInfo disableListInfo = disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()); - log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}", - disableListInfo.get(), scriptId, t, t.getCause()); - log.error("onScriptExecutionError", t); + log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}, scriptBody {}", + disableListInfo.get(), scriptId, t, t.getCause(), scriptBody); // if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { // log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test // disableListInfo.get(), diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java index f9fd3db977..15a3cf1c15 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java @@ -160,7 +160,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer return ((Invocable) engine).invokeFunction(functionName, args); } } catch (Exception e) { - onScriptExecutionError(scriptId, e); + onScriptExecutionError(scriptId, e, functionName); throw new ExecutionException(e); } }); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index b8bfeea75b..5b3b1f5e23 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.script; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -161,7 +160,8 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Override protected ListenableFuture doInvokeFunction(UUID scriptId, String functionName, Object[] args) { - String scriptBody = scriptIdToBodysMap.get(scriptId); + log.trace("doInvokeFunction js-request for uuid {} with timeout {}ms", scriptId, maxRequestsTimeout); + final String scriptBody = scriptIdToBodysMap.get(scriptId); if (scriptBody == null) { return Futures.immediateFailedFuture(new RuntimeException("No script body found for scriptId: [" + scriptId + "]!")); } @@ -170,7 +170,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { .setScriptIdLSB(scriptId.getLeastSignificantBits()) .setFunctionName(functionName) .setTimeout((int) maxRequestsTimeout) - .setScriptBody(scriptIdToBodysMap.get(scriptId)); + .setScriptBody(scriptBody); for (Object arg : args) { jsRequestBuilder.addArgs(arg.toString()); @@ -193,7 +193,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @Override public void onFailure(Throwable t) { - onScriptExecutionError(scriptId, t); + onScriptExecutionError(scriptId, t, scriptBody); if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { queueTimeoutMsgs.incrementAndGet(); } @@ -206,7 +206,7 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { return invokeResult.getResult(); } else { final RuntimeException e = new RuntimeException(invokeResult.getErrorDetails()); - onScriptExecutionError(scriptId, e); + onScriptExecutionError(scriptId, e, scriptBody); log.debug("[{}] Failed to compile script due to [{}]: {}", scriptId, invokeResult.getErrorCode().name(), invokeResult.getErrorDetails()); throw e; } From 7e551cc92a94aef3727f5732837846c588a4805b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 13 May 2021 20:23:24 +0300 Subject: [PATCH 083/151] log: reduced severity to trace for DefaultTbQueueRequestTemplate.setupNextCleanup() --- .../server/queue/common/DefaultTbQueueRequestTemplate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index abeb739160..7fa3ac5989 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -143,7 +143,7 @@ public class DefaultTbQueueRequestTemplate doPoll() { From ed44ac8f19e14a652978123147f9869e478c2958 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:27:19 +0300 Subject: [PATCH 084/151] test fixed for DefaultTbQueueRequestTemplateTest --- .../queue/common/DefaultTbQueueRequestTemplateTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index e60e4071f3..c73be715fe 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -37,7 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; @@ -55,12 +55,11 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.longThat; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -70,6 +69,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.hamcrest.MockitoHamcrest.longThat; @Slf4j @RunWith(MockitoJUnitRunner.class) From 63c906250c39799a8b0142c529d6b21d71d52477 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:27:52 +0300 Subject: [PATCH 085/151] log cleanup DefaultTbQueueRequestTemplate --- .../server/queue/common/DefaultTbQueueRequestTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index 7fa3ac5989..7463c11df5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -245,7 +245,7 @@ public class DefaultTbQueueRequestTemplate future, ResponseMetaData responseMetaData) { - log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, String.valueOf(request).replace("\n", " ")); + log.trace("[{}] Sending request, key [{}], expTime [{}], request {}", requestId, request.getKey(), responseMetaData.expTime, request); if (messagesStats != null) { messagesStats.incrementTotal(); } @@ -255,7 +255,7 @@ public class DefaultTbQueueRequestTemplate Date: Thu, 17 Jun 2021 13:34:06 +0300 Subject: [PATCH 086/151] code cleanup --- .../server/actors/service/DefaultActorService.java | 1 - .../server/service/script/AbstractJsInvokeService.java | 6 ------ 2 files changed, 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index 98f739a24b..7a28c205bc 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -111,7 +111,6 @@ public class DefaultActorService extends TbApplicationEventListener new DisableListInfo()); log.warn("Script has exception and will increment counter {} on disabledFunctions for id {}, exception {}, cause {}, scriptBody {}", disableListInfo.get(), scriptId, t, t.getCause(), scriptBody); -// if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { -// log.warn("Script has TimeoutException and will increment counter {} on disabledFunctions for id {}", //TODO remove after test -// disableListInfo.get(), -// scriptId); -// //return; //timeout is not a good reason to disable function -// } disableListInfo.incrementAndGet(); } From 75603e58bec47a8b30292cdaa57b03831547514f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:34:54 +0300 Subject: [PATCH 087/151] TbStopWatch updated licence header for CE --- .../thingsboard/common/util/TbStopWatch.java | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java index 4f5d1346a9..90f58ce7f2 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java +++ b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java @@ -1,32 +1,17 @@ /** - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * Copyright © 2016-2021 The Thingsboard Authors * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.thingsboard.common.util; From aaedb9e879ccd1ad09b14d5cc717ec981f6d1927 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:35:20 +0300 Subject: [PATCH 088/151] DefaultTbQueueRequestTemplateTest updated licence header for CE --- .../DefaultTbQueueRequestTemplateTest.java | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index c73be715fe..e50e204adb 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -1,32 +1,17 @@ /** - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * Copyright © 2016-2021 The Thingsboard Authors * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.thingsboard.server.queue.common; From bec228bc83d1541ed69c92ebb77fd13bb606eb47 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:42:37 +0300 Subject: [PATCH 089/151] test: removed unnecessary stubbing on DefaultTbQueueRequestTemplateTest --- .../queue/common/DefaultTbQueueRequestTemplateTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java index e50e204adb..9979e9ac43 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplateTest.java @@ -105,10 +105,7 @@ public class DefaultTbQueueRequestTemplateTest { @Test public void givenExternalExecutor_whenInitStop_thenOK() { - willDoNothing().given(inst).mainLoop(); - inst.init(); - //assertNotEquals(0, inst.tickTs); assertThat(inst.nextCleanupNs, equalTo(0L)); verify(queueAdmin, times(1)).createTopicIfNotExists(topic); verify(requestTemplate, times(1)).init(); @@ -184,7 +181,6 @@ public class DefaultTbQueueRequestTemplateTest { inst.init(); inst.setupNextCleanup(); willReturn(Collections.emptyList()).given(inst).doPoll(); - willDoNothing().given(inst).processResponse(any()); //when long stepNs = TimeUnit.MILLISECONDS.toNanos(1); From 8915b279a027b16c1d45d2e9728ee5dcf474fa7d Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 16 Jun 2021 12:40:38 +0300 Subject: [PATCH 090/151] js-script-engine-api: executeSwitch replaced with asynchronous executeSwitchAsync --- .../controller/RuleChainController.java | 2 +- .../script/RuleNodeJsScriptEngine.java | 12 ++++++--- .../rule/engine/api/ScriptEngine.java | 2 +- .../rule/engine/filter/TbJsSwitchNode.java | 27 ++++++++++++------- .../engine/filter/TbJsSwitchNodeTest.java | 2 +- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 3b9af2f0f2..39692922c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -400,7 +400,7 @@ public class RuleChainController extends BaseController { output = Boolean.toString(result); break; case "switch": - Set states = engine.executeSwitch(inMsg); + Set states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); output = objectMapper.writeValueAsString(states); break; case "json": diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index c688d6978d..e9cf8d621f 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -195,9 +195,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S }, MoreExecutors.directExecutor()); } - @Override - public Set executeSwitch(TbMsg msg) throws ScriptException { - JsonNode result = executeScript(msg); + Set executeSwitchPostProcessFunction(JsonNode result) throws ScriptException { if (result.isTextual()) { return Collections.singleton(result.asText()); } else if (result.isArray()) { @@ -217,6 +215,14 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S } } + @Override + public ListenableFuture> executeSwitchAsync(TbMsg msg) { + log.trace("execute switch async, msg {}", msg); + return Futures.transformAsync(executeScriptAsync(msg), + result -> Futures.immediateFuture(executeSwitchPostProcessFunction(result)), + MoreExecutors.directExecutor()); //usually runs in a callbackExecutor + } + private JsonNode executeScript(TbMsg msg) throws ScriptException { try { String[] inArgs = prepareArgs(msg); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java index a5dcf535e4..da9bd93f2b 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java @@ -35,7 +35,7 @@ public interface ScriptEngine { ListenableFuture executeFilterAsync(TbMsg msg); - Set executeSwitch(TbMsg msg) throws ScriptException; + ListenableFuture> executeSwitchAsync(TbMsg msg); JsonNode executeJson(TbMsg msg) throws ScriptException; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index f677b90158..5af4ccb29d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -15,7 +15,11 @@ */ package org.thingsboard.rule.engine.filter; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.ScriptEngine; @@ -58,17 +62,20 @@ public class TbJsSwitchNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ListeningExecutor jsExecutor = ctx.getJsExecutor(); ctx.logJsEvalRequest(); - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(msg)), - result -> { - ctx.logJsEvalResponse(); - processSwitch(ctx, msg, result); - }, - t -> { - ctx.logJsEvalFailure(); - ctx.tellFailure(msg, t); - }, ctx.getDbCallbackExecutor()); + Futures.addCallback(jsEngine.executeSwitchAsync(msg), new FutureCallback>() { + @Override + public void onSuccess(@Nullable Set result) { + ctx.logJsEvalResponse(); + processSwitch(ctx, msg, result); + } + + @Override + public void onFailure(Throwable t) { + ctx.logJsEvalFailure(); + ctx.tellFailure(msg, t); + } + }, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor } private void processSwitch(TbContext ctx, TbMsg msg, Set nextRelations) { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index cfacf4cb85..fc2e5aa041 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -73,7 +73,7 @@ public class TbJsSwitchNodeTest { TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); mockJsExecutor(); - when(scriptEngine.executeSwitch(msg)).thenReturn(Sets.newHashSet("one", "three")); + when(scriptEngine.executeSwitchAsync(msg)).thenReturn(Futures.immediateFuture(Sets.newHashSet("one", "three"))); node.onMsg(ctx, msg); verify(ctx).getJsExecutor(); From f3757ad127cd0029e347d7b5858a99e4eab62f93 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 16 Jun 2021 18:41:18 +0300 Subject: [PATCH 091/151] js-script-engine-api: refactored executeSwitchAsync --- .../script/RuleNodeJsScriptEngine.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index e9cf8d621f..2e6b48693a 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.script; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -32,6 +31,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import javax.script.ScriptException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -145,7 +145,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S return Futures.transformAsync(executeScriptAsync(prevMsg), result -> { if (!result.isObject()) { log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); + Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); } return Futures.immediateFuture(unbindMsg(result, prevMsg)); }, MoreExecutors.directExecutor()); @@ -195,31 +195,31 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S }, MoreExecutors.directExecutor()); } - Set executeSwitchPostProcessFunction(JsonNode result) throws ScriptException { + ListenableFuture> executeSwitchPostProcessAsyncFunction(JsonNode result) { if (result.isTextual()) { - return Collections.singleton(result.asText()); - } else if (result.isArray()) { - Set nextStates = Sets.newHashSet(); + return Futures.immediateFuture(Collections.singleton(result.asText())); + } + if (result.isArray()) { + Set nextStates = new HashSet<>(); for (JsonNode val : result) { if (!val.isTextual()) { log.warn("Wrong result type: {}", val.getNodeType()); - throw new ScriptException("Wrong result type: " + val.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + val.getNodeType())); } else { nextStates.add(val.asText()); } } - return nextStates; - } else { - log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); + return Futures.immediateFuture(nextStates); } + log.warn("Wrong result type: {}", result.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); } @Override public ListenableFuture> executeSwitchAsync(TbMsg msg) { log.trace("execute switch async, msg {}", msg); return Futures.transformAsync(executeScriptAsync(msg), - result -> Futures.immediateFuture(executeSwitchPostProcessFunction(result)), + this::executeSwitchPostProcessAsyncFunction, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor } From b397dfb518e101558790127b5e7ad9a7e7aef34f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 16 Jun 2021 20:49:46 +0300 Subject: [PATCH 092/151] js-script-engine-api: js sync calls replaced with completely async calls (CE API only) --- .../controller/RuleChainController.java | 8 +- .../script/RuleNodeJsScriptEngine.java | 140 +++++++----------- .../rule/engine/api/ScriptEngine.java | 9 +- .../rule/engine/api/TbContext.java | 4 + .../rule/engine/action/TbLogNode.java | 30 ++-- .../rule/engine/filter/TbJsSwitchNode.java | 2 - .../engine/filter/TbJsSwitchNodeTest.java | 17 +-- 7 files changed, 82 insertions(+), 128 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 39692922c1..fbfd0d595f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -390,13 +390,13 @@ public class RuleChainController extends BaseController { TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); switch (scriptType) { case "update": - output = msgToOutput(engine.executeUpdate(inMsg)); + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); break; case "generate": output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); break; case "filter": - boolean result = engine.executeFilter(inMsg); + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); output = Boolean.toString(result); break; case "switch": @@ -404,11 +404,11 @@ public class RuleChainController extends BaseController { output = objectMapper.writeValueAsString(states); break; case "json": - JsonNode json = engine.executeJson(inMsg); + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); output = objectMapper.writeValueAsString(json); break; case "string": - output = engine.executeToString(inMsg); + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); break; default: throw new IllegalArgumentException("Unsupported script type: " + scriptType); diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index 2e6b48693a..17e999a533 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -106,96 +106,77 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S } } - @Override - public List executeUpdate(TbMsg msg) throws ScriptException { - JsonNode result = executeScript(msg); - if (result.isObject()) { - return Collections.singletonList(unbindMsg(result, msg)); - } else if (result.isArray()){ - List res = new ArrayList<>(result.size()); - result.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg))); - return res; - } else { - log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); - } - } - @Override public ListenableFuture> executeUpdateAsync(TbMsg msg) { ListenableFuture result = executeScriptAsync(msg); - return Futures.transformAsync(result, json -> { - if (json.isObject()) { - return Futures.immediateFuture(Collections.singletonList(unbindMsg(json, msg))); - } else if (json.isArray()){ - List res = new ArrayList<>(json.size()); - json.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg))); - return Futures.immediateFuture(res); - } - else{ - log.warn("Wrong result type: {}", json.getNodeType()); - return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType())); - } - }, MoreExecutors.directExecutor()); + return Futures.transformAsync(result, + json -> executeUpdateTransform(msg, json), + MoreExecutors.directExecutor()); + } + + ListenableFuture> executeUpdateTransform(TbMsg msg, JsonNode json) { + if (json.isObject()) { + return Futures.immediateFuture(Collections.singletonList(unbindMsg(json, msg))); + } else if (json.isArray()) { + List res = new ArrayList<>(json.size()); + json.forEach(jsonObject -> res.add(unbindMsg(jsonObject, msg))); + return Futures.immediateFuture(res); + } + log.warn("Wrong result type: {}", json.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType())); } @Override public ListenableFuture executeGenerateAsync(TbMsg prevMsg) { - log.trace("execute generate async, prevMsg {}", prevMsg); - return Futures.transformAsync(executeScriptAsync(prevMsg), result -> { - if (!result.isObject()) { - log.warn("Wrong result type: {}", result.getNodeType()); - Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); - } - return Futures.immediateFuture(unbindMsg(result, prevMsg)); - }, MoreExecutors.directExecutor()); - + return Futures.transformAsync(executeScriptAsync(prevMsg), + result -> executeGenerateTransform(prevMsg, result), + MoreExecutors.directExecutor()); } - @Override - public JsonNode executeJson(TbMsg msg) throws ScriptException { - return executeScript(msg); + ListenableFuture executeGenerateTransform(TbMsg prevMsg, JsonNode result) { + if (!result.isObject()) { + log.warn("Wrong result type: {}", result.getNodeType()); + Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); + } + return Futures.immediateFuture(unbindMsg(result, prevMsg)); } @Override - public ListenableFuture executeJsonAsync(TbMsg msg) throws ScriptException { + public ListenableFuture executeJsonAsync(TbMsg msg) { return executeScriptAsync(msg); } @Override - public String executeToString(TbMsg msg) throws ScriptException { - JsonNode result = executeScript(msg); - if (!result.isTextual()) { - log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); - } - return result.asText(); + public ListenableFuture executeToStringAsync(TbMsg msg) { + return Futures.transformAsync(executeScriptAsync(msg), + this::executeToStringTransform, + MoreExecutors.directExecutor()); } - @Override - public boolean executeFilter(TbMsg msg) throws ScriptException { - JsonNode result = executeScript(msg); - if (!result.isBoolean()) { - log.warn("Wrong result type: {}", result.getNodeType()); - throw new ScriptException("Wrong result type: " + result.getNodeType()); + ListenableFuture executeToStringTransform(JsonNode result) { + if (result.isTextual()) { + return Futures.immediateFuture(result.asText()); } - return result.asBoolean(); + log.warn("Wrong result type: {}", result.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); } @Override public ListenableFuture executeFilterAsync(TbMsg msg) { - ListenableFuture result = executeScriptAsync(msg); - return Futures.transformAsync(result, json -> { - if (!json.isBoolean()) { - log.warn("Wrong result type: {}", json.getNodeType()); - return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType())); - } else { - return Futures.immediateFuture(json.asBoolean()); - } - }, MoreExecutors.directExecutor()); + return Futures.transformAsync(executeScriptAsync(msg), + this::executeFilterTransform, + MoreExecutors.directExecutor()); + } + + ListenableFuture executeFilterTransform(JsonNode json) { + if (json.isBoolean()) { + return Futures.immediateFuture(json.asBoolean()); + } + log.warn("Wrong result type: {}", json.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + json.getNodeType())); } - ListenableFuture> executeSwitchPostProcessAsyncFunction(JsonNode result) { + ListenableFuture> executeSwitchTransform(JsonNode result) { if (result.isTextual()) { return Futures.immediateFuture(Collections.singleton(result.asText())); } @@ -217,34 +198,19 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S @Override public ListenableFuture> executeSwitchAsync(TbMsg msg) { - log.trace("execute switch async, msg {}", msg); return Futures.transformAsync(executeScriptAsync(msg), - this::executeSwitchPostProcessAsyncFunction, + this::executeSwitchTransform, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor } - private JsonNode executeScript(TbMsg msg) throws ScriptException { - try { - String[] inArgs = prepareArgs(msg); - String eval = sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString(); - return mapper.readTree(eval); - } catch (ExecutionException e) { - if (e.getCause() instanceof ScriptException) { - throw (ScriptException) e.getCause(); - } else if (e.getCause() instanceof RuntimeException) { - throw new ScriptException(e.getCause().getMessage()); - } else { - throw new ScriptException(e); - } - } catch (Exception e) { - throw new ScriptException(e); - } - } - - private ListenableFuture executeScriptAsync(TbMsg msg) { + ListenableFuture executeScriptAsync(TbMsg msg) { log.trace("execute script async, msg {}", msg); String[] inArgs = prepareArgs(msg); - return Futures.transformAsync(sandboxService.invokeFunction(tenantId, msg.getCustomerId(), this.scriptId, inArgs[0], inArgs[1], inArgs[2]), + return executeScriptAsync(msg.getCustomerId(), inArgs[0], inArgs[1], inArgs[2]); + } + + ListenableFuture executeScriptAsync(CustomerId customerId, Object... args) { + return Futures.transformAsync(sandboxService.invokeFunction(tenantId, customerId, this.scriptId, args), o -> { try { return Futures.immediateFuture(mapper.readTree(o.toString())); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java index da9bd93f2b..012e135c60 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java @@ -19,14 +19,11 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.msg.TbMsg; -import javax.script.ScriptException; import java.util.List; import java.util.Set; public interface ScriptEngine { - List executeUpdate(TbMsg msg) throws ScriptException; - ListenableFuture> executeUpdateAsync(TbMsg msg); ListenableFuture executeGenerateAsync(TbMsg prevMsg); @@ -37,11 +34,9 @@ public interface ScriptEngine { ListenableFuture> executeSwitchAsync(TbMsg msg); - JsonNode executeJson(TbMsg msg) throws ScriptException; - - ListenableFuture executeJsonAsync(TbMsg msg) throws ScriptException; + ListenableFuture executeJsonAsync(TbMsg msg); - String executeToString(TbMsg msg) throws ScriptException; + ListenableFuture executeToStringAsync(TbMsg msg); void destroy(); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 43e3a5e329..62ed9b0fa0 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -214,6 +214,10 @@ public interface TbContext { EdgeEventService getEdgeEventService(); + /** + * Js script executors call are completely asynchronous + * */ + @Deprecated ListeningExecutor getJsExecutor(); ListeningExecutor getMailExecutor(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index 2b2f0d76a4..c981d5062d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -15,8 +15,11 @@ */ package org.thingsboard.rule.engine.action; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.ListeningExecutor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.TbContext; @@ -55,18 +58,21 @@ public class TbLogNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ListeningExecutor jsExecutor = ctx.getJsExecutor(); ctx.logJsEvalRequest(); - withCallback(jsExecutor.executeAsync(() -> jsEngine.executeToString(msg)), - toString -> { - ctx.logJsEvalResponse(); - log.info(toString); - ctx.tellSuccess(msg); - }, - t -> { - ctx.logJsEvalResponse(); - ctx.tellFailure(msg, t); - }); + Futures.addCallback(jsEngine.executeToStringAsync(msg), new FutureCallback() { + @Override + public void onSuccess(@Nullable String result) { + ctx.logJsEvalResponse(); + log.info(result); + ctx.tellSuccess(msg); + } + + @Override + public void onFailure(Throwable t) { + ctx.logJsEvalResponse(); + ctx.tellFailure(msg, t); + } + }, MoreExecutors.directExecutor()); //usually js responses runs on js callback executor } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 5af4ccb29d..bc900a0f2d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -33,8 +33,6 @@ import org.thingsboard.server.common.msg.TbMsg; import java.util.Set; -import static org.thingsboard.common.util.DonAsynchron.withCallback; - @Slf4j @RuleNode( type = ComponentType.FILTER, diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index fc2e5aa041..4ac81120b0 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -64,7 +64,7 @@ public class TbJsSwitchNodeTest { private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); @Test - public void multipleRoutesAreAllowed() throws TbNodeException, ScriptException { + public void multipleRoutesAreAllowed() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("temp", "10"); @@ -72,11 +72,9 @@ public class TbJsSwitchNodeTest { String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); - mockJsExecutor(); when(scriptEngine.executeSwitchAsync(msg)).thenReturn(Futures.immediateFuture(Sets.newHashSet("one", "three"))); node.onMsg(ctx, msg); - verify(ctx).getJsExecutor(); verify(ctx).tellNext(msg, Sets.newHashSet("one", "three")); } @@ -92,19 +90,6 @@ public class TbJsSwitchNodeTest { node.init(ctx, nodeConfiguration); } - @SuppressWarnings("unchecked") - private void mockJsExecutor() { - when(ctx.getJsExecutor()).thenReturn(executor); - doAnswer((Answer>>) invocationOnMock -> { - try { - Callable task = (Callable) (invocationOnMock.getArguments())[0]; - return Futures.immediateFuture((Set) task.call()); - } catch (Throwable th) { - return Futures.immediateFailedFuture(th); - } - }).when(executor).executeAsync(ArgumentMatchers.any(Callable.class)); - } - private void verifyError(TbMsg msg, String message, Class expectedClass) { ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); verify(ctx).tellFailure(same(msg), captor.capture()); From 966b0923e7e293c0bd5fb6aa3f98d43aa9dc93de Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 14:12:33 +0300 Subject: [PATCH 093/151] js-script-engine-api: fixed CE merge --- .../server/service/script/RuleNodeJsScriptEngine.java | 1 + .../main/java/org/thingsboard/rule/engine/api/ScriptEngine.java | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java index 17e999a533..4ab81702d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbMsg; diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java index 012e135c60..7ff63319cd 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java @@ -28,8 +28,6 @@ public interface ScriptEngine { ListenableFuture executeGenerateAsync(TbMsg prevMsg); - boolean executeFilter(TbMsg msg) throws ScriptException; - ListenableFuture executeFilterAsync(TbMsg msg); ListenableFuture> executeSwitchAsync(TbMsg msg); From 6a63b6e31dd02b9b63178f8bdf77be2cc8740b78 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 17 Jun 2021 14:16:46 +0300 Subject: [PATCH 094/151] fix to entity node level --- .../widget/lib/entities-hierarchy-widget.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts index 5fb11d025b..03d97c50fb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts @@ -443,7 +443,7 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O const dataPageData = subscription.dataPages[0]; const childNodes: HierarchyNavTreeNode[] = []; datasourcesPageData.data.forEach((childDatasource, index) => { - childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, dataPageData.data[index])); + childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, dataPageData.data[index], nodeCtx)); }); nodeCtx.childrenNodesLoaded = true; childrenNodesLoadCb(this.prepareNodes(childNodes)); From 29910fad8f6634e4fa7cfc1db2cf74d2e723f7d8 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 17 Jun 2021 13:34:54 +0300 Subject: [PATCH 095/151] TbStopWatch updated licence header for CE --- .../thingsboard/common/util/TbStopWatch.java | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java index b7dbd0052c..155ffb5360 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java +++ b/common/util/src/main/java/org/thingsboard/common/util/TbStopWatch.java @@ -1,32 +1,17 @@ /** - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * Copyright © 2016-2021 The Thingsboard Authors * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.thingsboard.common.util; From 75801045cfcdda85b90973d7e3079b22b747de52 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 17 Jun 2021 15:08:00 +0300 Subject: [PATCH 096/151] UI: Add SNMP device profile configuration --- .../home/components/home-components.module.ts | 5 +- ...ofile-transport-configuration.component.ts | 4 +- ...ile-transport-configuration.component.html | 23 -- ...rofile-communication-config.component.html | 72 ++++++ ...rofile-communication-config.component.scss | 44 ++++ ...-profile-communication-config.component.ts | 218 ++++++++++++++++++ ...snmp-device-profile-mapping.component.html | 81 +++++++ ...snmp-device-profile-mapping.component.scss | 46 ++++ .../snmp-device-profile-mapping.component.ts | 165 +++++++++++++ ...ile-transport-configuration.component.html | 46 ++++ ...ofile-transport-configuration.component.ts | 57 +++-- .../snmp-device-profile-transport.module.ts | 38 +++ ui-ngx/src/app/shared/models/constants.ts | 16 ++ ui-ngx/src/app/shared/models/device.models.ts | 37 ++- .../assets/locale/locale.constant-en_US.json | 25 ++ 15 files changed, 834 insertions(+), 43 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html rename ui-ngx/src/app/modules/home/components/profile/device/{ => snpm}/snmp-device-profile-transport-configuration.component.ts (66%) create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 3958830088..d0ad42b62e 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -99,7 +99,6 @@ import { DeviceProfileDialogComponent } from '@home/components/profile/device-pr import { DeviceProfileAutocompleteComponent } from '@home/components/profile/device-profile-autocomplete.component'; import { MqttDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/mqtt-device-profile-transport-configuration.component'; import { CoapDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/coap-device-profile-transport-configuration.component'; -import { SnmpDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/snmp-device-profile-transport-configuration.component'; import { DeviceProfileAlarmsComponent } from '@home/components/profile/alarm/device-profile-alarms.component'; import { DeviceProfileAlarmComponent } from '@home/components/profile/alarm/device-profile-alarm.component'; import { CreateAlarmRulesComponent } from '@home/components/profile/alarm/create-alarm-rules.component'; @@ -143,6 +142,7 @@ import { SecurityConfigLwm2mComponent } from '@home/components/device/security-c import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component'; import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component'; import { WidgetContainerComponent } from '@home/components/widget/widget-container.component'; +import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module'; @NgModule({ declarations: @@ -228,7 +228,6 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain DefaultDeviceProfileTransportConfigurationComponent, MqttDeviceProfileTransportConfigurationComponent, CoapDeviceProfileTransportConfigurationComponent, - SnmpDeviceProfileTransportConfigurationComponent, DeviceProfileTransportConfigurationComponent, CreateAlarmRulesComponent, AlarmRuleComponent, @@ -272,6 +271,7 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain SharedModule, SharedHomeComponentsModule, Lwm2mProfileComponentsModule, + SnmpDeviceProfileTransportModule, StatesControllerModule ], exports: [ @@ -339,7 +339,6 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain DefaultDeviceProfileTransportConfigurationComponent, MqttDeviceProfileTransportConfigurationComponent, CoapDeviceProfileTransportConfigurationComponent, - SnmpDeviceProfileTransportConfigurationComponent, DeviceProfileTransportConfigurationComponent, CreateAlarmRulesComponent, AlarmRuleComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index 305a0a84b6..50b3bc801c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -89,7 +89,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu if (configuration) { delete configuration.type; } - this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); + setTimeout(() => { + this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); + }); } private updateModel() { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html deleted file mode 100644 index f57b1ac716..0000000000 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html +++ /dev/null @@ -1,23 +0,0 @@ - -
- - -
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html new file mode 100644 index 0000000000..7d2f8a154c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html @@ -0,0 +1,72 @@ + +
+
+
+ + device-profile.snmp.scope + + + {{ snmpSpecTypeTranslationMap.get(snmpSpecType) }} + + + + {{ 'device-profile.snmp.scope-required' | translate }} + + + +
+ + device-profile.snmp.querying-frequency + + + {{ 'device-profile.snmp.querying-frequency-required' | translate }} + + + {{ 'device-profile.snmp.querying-frequency-invalid-format' | translate }} + + + + +
+
+ +
+
+ device-profile.snmp.please-add-communication-config +
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss new file mode 100644 index 0000000000..3b6661b862 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .communication-config { + border: 2px groove rgba(0, 0, 0, 0.25); + border-radius: 4px; + padding: 8px; + min-width: 0; + } + + .scope-row { + padding-bottom: 8px; + } + + .required-text { + margin: 16px 0 + } +} + +:host ::ng-deep { + .mat-form-field.spec { + .mat-form-field-infix { + width: 160px; + } + } + .button-icon{ + font-size: 20px; + width: 20px; + height: 20px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts new file mode 100644 index 0000000000..d683d46880 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts @@ -0,0 +1,218 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, + Validators +} from '@angular/forms'; +import { SnmpCommunicationConfig, SnmpSpecType, SnmpSpecTypeTranslationMap } from '@shared/models/device.models'; +import { Subject, Subscription } from 'rxjs'; +import { isUndefinedOrNull } from '@core/utils'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'tb-snmp-device-profile-communication-config', + templateUrl: './snmp-device-profile-communication-config.component.html', + styleUrls: ['./snmp-device-profile-communication-config.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SnmpDeviceProfileCommunicationConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SnmpDeviceProfileCommunicationConfigComponent), + multi: true + }] +}) +export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator { + + snmpSpecTypes = Object.values(SnmpSpecType); + snmpSpecTypeTranslationMap = SnmpSpecTypeTranslationMap; + + deviceProfileCommunicationConfig: FormGroup; + + @Input() + disabled: boolean; + + private usedSpecType: SnmpSpecType[] = []; + private valueChange$: Subscription = null; + private destroy$ = new Subject(); + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder) { } + + ngOnInit(): void { + this.deviceProfileCommunicationConfig = this.fb.group({ + communicationConfig: this.fb.array([]) + }); + } + + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + communicationConfigFormArray(): FormArray { + return this.deviceProfileCommunicationConfig.get('communicationConfig') as FormArray; + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + if (this.disabled) { + this.deviceProfileCommunicationConfig.disable({emitEvent: false}); + } else { + this.deviceProfileCommunicationConfig.enable({emitEvent: false}); + } + } + + writeValue(communicationConfig: SnmpCommunicationConfig[]) { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + const communicationConfigControl: Array = []; + if (communicationConfig) { + communicationConfig.forEach((config) => { + communicationConfigControl.push(this.createdFormGroup(config)); + }); + } + this.deviceProfileCommunicationConfig.setControl('communicationConfig', this.fb.array(communicationConfigControl)); + if (!communicationConfig || !communicationConfig.length) { + this.addCommunicationConfig(); + } + if (this.disabled) { + this.deviceProfileCommunicationConfig.disable({emitEvent: false}); + } else { + this.deviceProfileCommunicationConfig.enable({emitEvent: false}); + } + this.valueChange$ = this.deviceProfileCommunicationConfig.valueChanges.subscribe(() => { + this.updateModel(); + }); + this.updateUsedSpecType(); + if (!this.disabled && !this.deviceProfileCommunicationConfig.valid) { + this.updateModel(); + } + } + + public validate() { + return this.deviceProfileCommunicationConfig.valid && this.deviceProfileCommunicationConfig.value.communicationConfig.length ? null : { + communicationConfig: false + }; + } + + public removeCommunicationConfig(index: number) { + this.communicationConfigFormArray().removeAt(index); + } + + + get isAddEnabled(): boolean { + return this.communicationConfigFormArray().length !== Object.keys(SnmpSpecType).length; + } + + public addCommunicationConfig() { + this.communicationConfigFormArray().push(this.createdFormGroup()); + this.deviceProfileCommunicationConfig.updateValueAndValidity(); + if (!this.deviceProfileCommunicationConfig.valid) { + this.updateModel(); + } + } + + private getFirstUnusedSeverity(): SnmpSpecType { + for (const type of Object.values(SnmpSpecType)) { + if (this.usedSpecType.indexOf(type) === -1) { + return type; + } + } + return null; + } + + public isDisabledSeverity(type: SnmpSpecType, index: number): boolean { + const usedIndex = this.usedSpecType.indexOf(type); + return usedIndex > -1 && usedIndex !== index; + } + + public isShowFrequency(type: SnmpSpecType): boolean { + return type === SnmpSpecType.TELEMETRY_QUERYING || type === SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING; + } + + private updateUsedSpecType() { + this.usedSpecType = []; + const value: SnmpCommunicationConfig[] = this.deviceProfileCommunicationConfig.get('communicationConfig').value; + value.forEach((rule, index) => { + this.usedSpecType[index] = rule.spec; + }); + } + + private createdFormGroup(value?: SnmpCommunicationConfig): FormGroup { + if (isUndefinedOrNull(value)) { + value = { + spec: this.getFirstUnusedSeverity(), + queryingFrequencyMs: 0, + mappings: null + }; + } + const form = this.fb.group({ + spec: [value.spec, Validators.required], + mappings: [value.mappings] + }); + if (this.isShowFrequency(value.spec)) { + form.addControl('queryingFrequencyMs', + this.fb.control(value.queryingFrequencyMs, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')])); + } + form.get('spec').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(spec => { + if (this.isShowFrequency(spec)) { + form.addControl('queryingFrequencyMs', + this.fb.control(0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')])); + } else { + form.removeControl('queryingFrequencyMs'); + } + }); + return form; + } + + private updateModel() { + const value: SnmpCommunicationConfig[] = this.deviceProfileCommunicationConfig.get('communicationConfig').value; + value.forEach(config => { + if (!this.isShowFrequency(config.spec)) { + delete config.queryingFrequencyMs; + } + }); + this.updateUsedSpecType(); + this.propagateChange(value); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html new file mode 100644 index 0000000000..a2a0a97d15 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html @@ -0,0 +1,81 @@ + +
+
+
+ + + + +
+
+ +
+
+ + + + + {{ dataTypesTranslationMap.get(dataType) | translate }} + + + + {{ 'device-profile.snmp.data-type-required' | translate }} + + + + + + + {{ 'device-profile.snmp.data-key-required' | translate }} + + + + + + + {{ 'device-profile.snmp.oid-required' | translate }} + + + {{ 'device-profile.snmp.oid-pattern' | translate }} + + + +
+
+
+ device-profile.snmp.please-add-mapping-config +
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss new file mode 100644 index 0000000000..94cf2290ac --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .mapping-config { + min-width: 518px; + } + .mapping-list { + padding-bottom: 8px; + height: 46px; + } + + .required-text { + margin: 14px 0; + } +} + +:host ::ng-deep { + .mapping-list { + mat-form-field { + .mat-form-field-wrapper { + padding-bottom: 0; + .mat-form-field-infix { + border-top-width: 0.2em; + width: auto; + min-width: auto; + } + .mat-form-field-underline { + bottom: 0; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts new file mode 100644 index 0000000000..53dbec422c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts @@ -0,0 +1,165 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormArray, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { SnmpMapping } from '@shared/models/device.models'; +import { Subscription } from 'rxjs'; +import { DataType, DataTypeTranslationMap } from '@shared/models/constants'; +import { isUndefinedOrNull } from '@core/utils'; + +@Component({ + selector: 'tb-snmp-device-profile-mapping', + templateUrl: './snmp-device-profile-mapping.component.html', + styleUrls: ['./snmp-device-profile-mapping.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SnmpDeviceProfileMappingComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SnmpDeviceProfileMappingComponent), + multi: true + }] +}) +export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator { + + mappingsConfigForm: FormGroup; + + dataTypes = Object.values(DataType); + dataTypesTranslationMap = DataTypeTranslationMap; + + @Input() + disabled: boolean; + + private readonly oidPattern: RegExp = /^\.?([0-2])((\.0)|(\.[1-9][0-9]*))*$/; + + private valueChange$: Subscription = null; + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder) { } + + ngOnInit() { + this.mappingsConfigForm = this.fb.group({ + mappings: this.fb.array([]) + }); + } + + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + } + + registerOnChange(fn: any) { + this.propagateChange = fn; + } + + registerOnTouched(fn: any) { + } + + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + if (this.disabled) { + this.mappingsConfigForm.disable({emitEvent: false}); + } else { + this.mappingsConfigForm.enable({emitEvent: false}); + } + } + + validate(): ValidationErrors | null { + return this.mappingsConfigForm.valid && this.mappingsConfigForm.value.mappings.length ? null : { + mapping: false + }; + } + + writeValue(mappings: SnmpMapping[]) { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + const mappingsControl: Array = []; + if (mappings) { + mappings.forEach((config) => { + mappingsControl.push(this.createdFormGroup(config)); + }); + } + this.mappingsConfigForm.setControl('mappings', this.fb.array(mappingsControl)); + if (!mappings || !mappings.length) { + this.addMappingConfig(); + } + if (this.disabled) { + this.mappingsConfigForm.disable({emitEvent: false}); + } else { + this.mappingsConfigForm.enable({emitEvent: false}); + } + this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => { + this.updateModel(); + }); + if (!this.disabled && !this.mappingsConfigForm.valid) { + this.updateModel(); + } + } + + mappingsConfigFormArray(): FormArray { + return this.mappingsConfigForm.get('mappings') as FormArray; + } + + public addMappingConfig() { + this.mappingsConfigFormArray().push(this.createdFormGroup()); + this.mappingsConfigForm.updateValueAndValidity(); + if (!this.mappingsConfigForm.valid) { + this.updateModel(); + } + } + + public removeMappingConfig(index: number) { + this.mappingsConfigFormArray().removeAt(index); + } + + private createdFormGroup(value?: SnmpMapping): FormGroup { + if (isUndefinedOrNull(value)) { + value = { + dataType: DataType.STRING, + key: '', + oid: '' + }; + } + return this.fb.group({ + dataType: [value.dataType, Validators.required], + key: [value.key, Validators.required], + oid: [value.oid, [Validators.required, Validators.pattern(this.oidPattern)]] + }); + } + + private updateModel() { + const value: SnmpMapping[] = this.mappingsConfigForm.get('mappings').value; + this.propagateChange(value); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html new file mode 100644 index 0000000000..7dad337d72 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html @@ -0,0 +1,46 @@ + +
+
+ + device-profile.snmp.timeout-ms + + + {{ 'device-profile.snmp.timeout-ms-required' | translate }} + + + {{ 'device-profile.snmp.timeout-ms-invalid-format' | translate }} + + + + device-profile.snmp.retries + + + {{ 'device-profile.snmp.retries-required' | translate }} + + + {{ 'device-profile.snmp.retries-invalid-format' | translate }} + + +
+
device-profile.snmp.communication-configs
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts similarity index 66% rename from ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts rename to ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts index e6749a9219..a384acbae3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts @@ -15,9 +15,16 @@ /// import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@app/core/core.state'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceProfileTransportConfiguration, @@ -40,19 +47,24 @@ export interface OidMappingConfiguration { selector: 'tb-snmp-device-profile-transport-configuration', templateUrl: './snmp-device-profile-transport-configuration.component.html', styleUrls: [], - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent), + multi: true + }] }) -export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { +export class SnmpDeviceProfileTransportConfigurationComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator { snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; private destroy$ = new Subject(); private requiredValue: boolean; - private configuration = []; get required(): boolean { return this.requiredValue; @@ -69,12 +81,14 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control private propagateChange = (v: any) => { } - constructor(private store: Store, private fb: FormBuilder) { + constructor(private fb: FormBuilder) { } ngOnInit(): void { this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ - configuration: [null, Validators.required] + timeoutMs: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + retries: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + communicationConfigs: [null, Validators.required], }); this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( takeUntil(this.destroy$) @@ -95,18 +109,33 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control registerOnTouched(fn: any): void { } + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + if (this.disabled) { + this.snmpDeviceProfileTransportConfigurationFormGroup.disable({emitEvent: false}); + } else { + this.snmpDeviceProfileTransportConfigurationFormGroup.enable({emitEvent: false}); + } + } + writeValue(value: SnmpDeviceProfileTransportConfiguration | null): void { if (isDefinedAndNotNull(value)) { - this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false}); + this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue(value, {emitEvent: !value.communicationConfigs}); } } private updateModel() { let configuration: DeviceProfileTransportConfiguration = null; if (this.snmpDeviceProfileTransportConfigurationFormGroup.valid) { - configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration; + configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue(); configuration.type = DeviceTransportType.SNMP; } this.propagateChange(configuration); } + + validate(): ValidationErrors | null { + return this.snmpDeviceProfileTransportConfigurationFormGroup.valid ? null : { + snmpDeviceProfileTransportConfiguration: false + }; + } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts new file mode 100644 index 0000000000..c0797d9b0e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgModule } from '@angular/core'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { SnmpDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component'; +import { SnmpDeviceProfileCommunicationConfigComponent } from './snmp-device-profile-communication-config.component'; +import { SnmpDeviceProfileMappingComponent } from './snmp-device-profile-mapping.component'; + +@NgModule({ + declarations: [ + SnmpDeviceProfileTransportConfigurationComponent, + SnmpDeviceProfileCommunicationConfigComponent, + SnmpDeviceProfileMappingComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + SnmpDeviceProfileTransportConfigurationComponent + ] +}) +export class SnmpDeviceProfileTransportModule { } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index b72e2b196d..cd81753c76 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -147,6 +147,22 @@ export enum ValueType { JSON = 'JSON' } +export enum DataType { + STRING = 'STRING', + LONG = 'LONG', + BOOLEAN = 'BOOLEAN', + DOUBLE = 'DOUBLE', + JSON = 'JSON' +} + +export const DataTypeTranslationMap = new Map([ + [DataType.STRING, 'value.string'], + [DataType.LONG, 'value.integer'], + [DataType.BOOLEAN, 'value.boolean'], + [DataType.DOUBLE, 'value.double'], + [DataType.JSON, 'value.json'] +]); + export const valueTypesMap = new Map( [ [ diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 7af8e56472..568faeb452 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -29,6 +29,7 @@ import * as _moment from 'moment'; import { AbstractControl, ValidationErrors } from '@angular/forms'; import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; +import { DataType } from '@shared/models/constants'; export enum DeviceProfileType { DEFAULT = 'DEFAULT', @@ -257,7 +258,35 @@ export interface Lwm2mDeviceProfileTransportConfiguration { } export interface SnmpDeviceProfileTransportConfiguration { - [key: string]: any; + timeoutMs?: number; + retries?: number; + communicationConfigs?: SnmpCommunicationConfig[]; +} + +export enum SnmpSpecType { + TELEMETRY_QUERYING = 'TELEMETRY_QUERYING', + CLIENT_ATTRIBUTES_QUERYING = 'CLIENT_ATTRIBUTES_QUERYING', + SHARED_ATTRIBUTES_SETTING = 'SHARED_ATTRIBUTES_SETTING', + TO_DEVICE_RPC_REQUEST = 'TO_DEVICE_RPC_REQUEST' +} + +export const SnmpSpecTypeTranslationMap = new Map([ + [SnmpSpecType.TELEMETRY_QUERYING, ' Telemetry'], + [SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING, 'Client attributes'], + [SnmpSpecType.SHARED_ATTRIBUTES_SETTING, 'Shared attributes'], + [SnmpSpecType.TO_DEVICE_RPC_REQUEST, 'RPC request'] +]); + +export interface SnmpCommunicationConfig { + spec: SnmpSpecType; + mappings: SnmpMapping[]; + queryingFrequencyMs?: number; +} + +export interface SnmpMapping { + oid: string; + key: string; + dataType: DataType; } export type DeviceProfileTransportConfigurations = DefaultDeviceProfileTransportConfiguration & @@ -332,7 +361,11 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M}; break; case DeviceTransportType.SNMP: - const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = {}; + const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = { + timeoutMs: 0, + retries: 0, + communicationConfigs: null + }; transportConfiguration = {...snmpTransportConfiguration, type: DeviceTransportType.SNMP}; break; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index ec084b8a3a..95dc0c2d72 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1297,6 +1297,31 @@ "sw-update-recourse": "Software update CoAP recourse", "sw-update-recourse-required": "Software update CoAP recourse is required.", "config-json-tab": "Json Config Profile Device" + }, + "snmp": { + "add-communication-config": "Add communication config", + "add-mapping": "Add mapping", + "communication-configs": "Communication configs", + "data-key": "Data key", + "data-key-required": "Data key is required.", + "data-type": "Data type", + "data-type-required": "Data type is required.", + "oid": "OID", + "oid-pattern": "Invalid OID format.", + "oid-required": "OID is required.", + "please-add-communication-config": "Please add communication config", + "please-add-mapping-config": "Please add mapping config", + "querying-frequency": "Querying frequency, ms", + "querying-frequency-invalid-format": "Querying frequency must be a positive integer.", + "querying-frequency-required": "Querying frequency is required.", + "retries": "Retries", + "retries-invalid-format": "Retries must be a positive integer.", + "retries-required": "Retries is required.", + "scope": "Scope", + "scope-required": "Scope is required.", + "timeout-ms": "Timeout, ms", + "timeout-ms-invalid-format": "Timeout must be a positive integer.", + "timeout-ms-required": "Timeout is required." } }, "dialog": { From 0c1e608051ac4e77cade38a27587635351e54737 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 17 Jun 2021 16:35:30 +0300 Subject: [PATCH 097/151] UI: Refactoring lwm2m object list --- .../device/lwm2m/lwm2m-object-list.component.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index ae7c3ccdc6..62d96b69e0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -89,6 +89,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V objectsList: [this.objectsList], objectLwm2m: [''] }); + this.lwm2mListFormGroup.valueChanges.subscribe((value) => { + let formValue = null; + if (this.lwm2mListFormGroup.valid) { + formValue = value; + } + this.propagateChange(formValue); + }); } private updateValidators = (): void => { @@ -142,7 +149,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.objectsList = []; this.modelValue = []; } - this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList, {emitEvents: false}); + this.lwm2mListFormGroup.patchValue({objectsList: this.objectsList}, {emitEvent: false}); this.dirty = false; } } @@ -195,9 +202,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } } - private clear = (): void => { + private clear() { this.searchText = ''; - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null); + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null, {emitEvent: false}); setTimeout(() => { this.objectInput.nativeElement.blur(); this.objectInput.nativeElement.focus(); From 248e35cb249eff03b25e8b983701181191e0ada1 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 17 Jun 2021 17:05:55 +0300 Subject: [PATCH 098/151] UI: Fixed not updated view entities table, after updated values --- .../home/components/widget/lib/alarms-table-widget.component.ts | 1 + .../components/widget/lib/entities-table-widget.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 138c56d307..390559bab3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -274,6 +274,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.updateTitle(true); this.alarmsDatasource.updateAlarms(); this.clearCache(); + this.ctx.detectChanges(); } public pageLinkSortDirection(): SortDirection { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index 8994051223..68db8a0dcc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -47,7 +47,6 @@ import { isDefined, isNumber, isObject, - isString, isUndefined } from '@core/utils'; import cssjs from '@core/css/css'; @@ -236,6 +235,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.updateTitle(true); this.entityDatasource.dataUpdated(); this.clearCache(); + this.ctx.detectChanges(); } public pageLinkSortDirection(): SortDirection { From bed454ec0fcace61736ca58129d1b74646fdffba Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 17 Jun 2021 18:29:33 +0300 Subject: [PATCH 099/151] LwM2M Refactoring of OTA --- .../src/main/resources/thingsboard.yml | 7 +- .../transport/TransportSqlTestSuite.java | 14 +- .../config/LwM2MTransportServerConfig.java | 20 +- .../server/DefaultLwM2mTransportService.java | 5 +- .../lwm2m/server/LwM2mNetworkConfig.java | 6 +- .../server/LwM2mTransportCoapResource.java | 9 +- .../lwm2m/server/LwM2mTransportUtil.java | 46 +- .../lwm2m/server/UpdateResultFw.java | 13 +- .../DefaultLwM2MAttributesService.java | 14 +- .../lwm2m/server/client/LwM2mClient.java | 22 - .../lwm2m/server/client/LwM2mFwSwUpdate.java | 486 ------------------ .../common/LwM2MExecutorAwareService.java | 41 ++ .../AbstractTbLwM2MRequestCallback.java | 12 +- .../DefaultLwM2mDownlinkMsgHandler.java | 62 ++- .../TbLwM2MCancelAllObserveCallback.java | 8 +- .../TbLwM2MCancelObserveCallback.java | 7 +- .../downlink/TbLwM2MDeleteCallback.java | 5 +- .../downlink/TbLwM2MDiscoverCallback.java | 5 +- .../downlink/TbLwM2MExecuteCallback.java | 5 +- .../downlink/TbLwM2MObserveCallback.java | 7 +- .../server/downlink/TbLwM2MReadCallback.java | 7 +- .../downlink/TbLwM2MTargetedCallback.java | 7 +- .../TbLwM2MUplinkTargetedCallback.java | 35 ++ .../TbLwM2MWriteAttributesCallback.java | 5 +- .../TbLwM2MWriteResponseCallback.java | 7 +- .../log/DefaultLwM2MTelemetryLogService.java | 56 ++ .../server/log/LwM2MTelemetryLogService.java | 26 + .../ota/DefaultLwM2MOtaUpdateService.java | 163 ++++-- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 23 +- .../server/ota/LwM2MOtaUpdateService.java | 6 +- .../rpc/DefaultLwM2MRpcRequestHandler.java | 22 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 197 ++----- .../server/uplink/LwM2mUplinkMsgHandler.java | 4 - .../src/main/resources/tb-lwm2m-transport.yml | 8 +- 34 files changed, 479 insertions(+), 881 deletions(-) delete mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/common/LwM2MExecutorAwareService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MUplinkTargetedCallback.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/LwM2MTelemetryLogService.java diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 54721ba2fa..fcde169fc9 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -677,12 +677,11 @@ transport: timeout: "${LWM2M_TIMEOUT:120000}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" - response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" - registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" + uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" + downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" + ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}" clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" - update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" - un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}" # Use redis for Security and Registration stores redis.enabled: "${LWM2M_REDIS_ENABLED:false}" diff --git a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java index d059ea1449..25df3bee00 100644 --- a/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java @@ -26,13 +26,13 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ -// "org.thingsboard.server.transport.*.rpc.sql.*Test", -// "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", -// "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", -// "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", -// "org.thingsboard.server.transport.*.attributes.request.sql.*Test", -// "org.thingsboard.server.transport.*.claim.sql.*Test", -// "org.thingsboard.server.transport.*.provision.sql.*Test", + "org.thingsboard.server.transport.*.rpc.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", + "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", + "org.thingsboard.server.transport.*.attributes.request.sql.*Test", + "org.thingsboard.server.transport.*.claim.sql.*Test", + "org.thingsboard.server.transport.*.provision.sql.*Test", "org.thingsboard.server.transport.lwm2m.*Test" }) public class TransportSqlTestSuite { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index 25c7766895..a2ff361712 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -57,29 +57,21 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { private boolean recommendedSupportedGroups; @Getter - @Value("${transport.lwm2m.response_pool_size:}") - private int responsePoolSize; + @Value("${transport.lwm2m.downlink_pool_size:}") + private int downlinkPoolSize; @Getter - @Value("${transport.lwm2m.registered_pool_size:}") - private int registeredPoolSize; + @Value("${transport.lwm2m.uplink_pool_size:}") + private int uplinkPoolSize; @Getter - @Value("${transport.lwm2m.registration_store_pool_size:}") - private int registrationStorePoolSize; + @Value("${transport.lwm2m.ota_pool_size:}") + private int otaPoolSize; @Getter @Value("${transport.lwm2m.clean_period_in_sec:}") private int cleanPeriodInSec; - @Getter - @Value("${transport.lwm2m.update_registered_pool_size:}") - private int updateRegisteredPoolSize; - - @Getter - @Value("${transport.lwm2m.un_registered_pool_size:}") - private int unRegisteredPoolSize; - @Getter @Value("${transport.lwm2m.security.key_store_type:}") private String keyStoreType; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 97ab82f802..9425ef7891 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -27,6 +27,7 @@ import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.springframework.stereotype.Component; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -80,6 +81,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; private final LwM2mTransportServerHelper helper; + private final OtaPackageDataCache otaPackageDataCache; private final DefaultLwM2MUplinkMsgHandler handler; private final CaliforniumRegistrationStore registrationStore; private final TbSecurityStore securityStore; @@ -103,8 +105,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { * "coap://host:port/{path}/{token}/{nameFile}" */ - - LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RECOURSE); + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RECOURSE); this.server.coap().getServer().add(otaCoapResource); this.startLhServer(); this.context.setServer(server); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java index 00f70dbcf7..4fc60aecfc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java @@ -84,10 +84,10 @@ public class LwM2mNetworkConfig { Create new instance of udp endpoint context matcher. Params: checkAddress - – true with address check, (STRICT, UDP) + – true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad - false, without */ - coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT"); + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); /** https://tools.ietf.org/html/rfc7959#section-2.9.3 The block size (number of bytes) to use when doing a blockwise transfer. \ @@ -103,7 +103,7 @@ public class LwM2mNetworkConfig { */ coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); - coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); return coapConfig; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index ff86ee9bd9..229ec1a08c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -24,6 +24,7 @@ import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.ResourceObserver; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @@ -39,11 +40,11 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); - private final LwM2mUplinkMsgHandler handler; + private final OtaPackageDataCache otaPackageDataCache; - public LwM2mTransportCoapResource(LwM2mUplinkMsgHandler handler, String name) { + public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name) { super(name); - this.handler = handler; + this.otaPackageDataCache = otaPackageDataCache; this.setObservable(true); // enable observing this.addObserver(new CoapResourceObserver()); } @@ -154,7 +155,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { } private byte[] getOtaData(UUID currentId) { - return ((DefaultLwM2MUplinkMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); + return otaPackageDataCache.get(currentId.toString()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index f310eb6481..d04e4993f1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -201,36 +201,12 @@ public class LwM2mTransportUtil { } } - /** - * FirmwareUpdateStatus { - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED - */ - public static OtaPackageUpdateStatus equalsFwSateFwResultToFirmwareUpdateStatus(UpdateStateFw updateStateFw, UpdateResultFw updateResultFw) { - switch (updateResultFw) { - case INITIAL: - return toOtaPackageUpdateStatus(updateStateFw); - case UPDATE_SUCCESSFULLY: - return UPDATED; - case NOT_ENOUGH: - case OUT_OFF_MEMORY: - case CONNECTION_LOST: - case INTEGRITY_CHECK_FAILURE: - case UNSUPPORTED_TYPE: - case INVALID_URI: - case UPDATE_FAILED: - case UNSUPPORTED_PROTOCOL: - return FAILED; - default: - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateFw.name(), updateResultFw.name()); - } - } - - public static OtaPackageUpdateStatus toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { + public static Optional toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { switch (updateResultFw) { case INITIAL: - return VERIFIED; + return Optional.empty(); case UPDATE_SUCCESSFULLY: - return UPDATED; + return Optional.of(UPDATED); case NOT_ENOUGH: case OUT_OFF_MEMORY: case CONNECTION_LOST: @@ -239,22 +215,22 @@ public class LwM2mTransportUtil { case INVALID_URI: case UPDATE_FAILED: case UNSUPPORTED_PROTOCOL: - return FAILED; + return Optional.of(FAILED); default: throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); } } - public static OtaPackageUpdateStatus toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { + public static Optional toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { switch (updateStateFw) { case IDLE: - return VERIFIED; + return Optional.empty(); case DOWNLOADING: - return DOWNLOADING; + return Optional.of(DOWNLOADING); case DOWNLOADED: - return DOWNLOADED; + return Optional.of(DOWNLOADED); case UPDATING: - return UPDATING; + return Optional.of(UPDATING); default: throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw); } @@ -471,7 +447,7 @@ public class LwM2mTransportUtil { return lwM2mOtaConvert; } else if (FW_RESULT_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); - lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type); + lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).getType()); return lwM2mOtaConvert; } else if (SW_UPDATE_STATE_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); @@ -585,7 +561,7 @@ public class LwM2mTransportUtil { public static String fromVersionedIdToObjectId(String pathIdVer) { try { - if(pathIdVer == null) { + if (pathIdVer == null) { return null; } String[] keyArray = pathIdVer.split(LWM2M_SEPARATOR_PATH); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java index 1b2e7d6b26..79361aeb1d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.transport.lwm2m.server; +import lombok.Getter; + /** * FW Update Result * 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value. @@ -40,14 +42,17 @@ public enum UpdateResultFw { UPDATE_FAILED(8, "Firmware update failed", false), UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false); - public int code; - public String type; - public boolean isAgain; + @Getter + private int code; + @Getter + private String type; + @Getter + private boolean again; UpdateResultFw(int code, String type, boolean isAgain) { this.code = code; this.type = type; - this.isAgain = isAgain; + this.again = isAgain; } public static UpdateResultFw fromUpdateResultFwByType(String type) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index ed717f3eee..ee723dbaa6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -39,6 +39,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @@ -71,6 +72,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { private final LwM2MTransportServerConfig config; private final LwM2mUplinkMsgHandler uplinkHandler; private final LwM2mDownlinkMsgHandler downlinkHandler; + private final LwM2MTelemetryLogService logService; private final LwM2MOtaUpdateService otaUpdateService; @Override @@ -150,14 +152,6 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { if (!otherAttributes.isEmpty()) { onAttributesUpdate(lwM2MClient, otherAttributes); } - } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { - msg.getSharedUpdatedList().forEach(tsKvProto -> { - String pathName = tsKvProto.getKv().getKey(); - Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { - lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); - } - }); } else if (lwM2MClient == null) { log.error("OnAttributeUpdate, lwM2MClient is null"); } @@ -197,12 +191,12 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); - downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, lwM2MClient, versionedId)); + downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, lwM2MClient, versionedId)); } else { log.error("Failed update resource [{}] [{}]", versionedId, newValue); String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", LOG_LWM2M_ERROR, versionedId, newValue); - uplinkHandler.logToTelemetry(lwM2MClient, logMsg); + logService.log(lwM2MClient, logMsg); log.info("Failed update resource [{}] [{}]", versionedId, newValue); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 21d6ec3d48..0e71a1e865 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -95,12 +95,6 @@ public class LwM2mClient implements Cloneable { private UUID profileId; @Getter @Setter - private volatile LwM2mFwSwUpdate fwUpdate; - @Getter - @Setter - private volatile LwM2mFwSwUpdate swUpdate; - @Getter - @Setter private Registration registration; private ValidateDeviceCredentialsResponse credentials; @@ -365,21 +359,5 @@ public class LwM2mClient implements Cloneable { } } - public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { - if (this.fwUpdate == null) { - var profile = clientContext.getProfile(this.getProfileId()); - this.fwUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); - } - return this.fwUpdate; - } - - public LwM2mFwSwUpdate getSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { - if (this.swUpdate == null) { - var profile = clientContext.getProfile(this.getProfileId()); - this.swUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); - } - return this.fwUpdate; - } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java deleted file mode 100644 index b39f6084cd..0000000000 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ /dev/null @@ -1,486 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.lwm2m.server.client; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.leshan.server.registration.Registration; -import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; -import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; - -import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; -import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UN_INSTALL_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.toOtaPackageUpdateStatus; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; - -@Slf4j -public class LwM2mFwSwUpdate { - // 5/0/6 PkgName - // 9/0/0 PkgName - @Getter - @Setter - private volatile String currentTitle; - // 5/0/7 PkgVersion - // 9/0/1 PkgVersion - @Getter - @Setter - private volatile String currentVersion; - @Getter - @Setter - private volatile UUID currentId; - @Getter - @Setter - private volatile String stateUpdate; - @Getter - private String pathPackageId; - @Getter - private String pathStateId; - @Getter - private String pathResultId; - @Getter - private String pathNameId; - @Getter - private String pathVerId; - @Getter - private String pathInstallId; - @Getter - private String pathUnInstallId; - @Getter - private String wUpdate; - @Getter - @Setter - private volatile boolean infoFwSwUpdate = false; - private final OtaPackageType type; - - private final LwM2mUplinkMsgHandler handler; - - @Getter - LwM2mClient lwM2MClient; - @Getter - private final List pendingInfoRequestsStart; - @Getter - @Setter - private volatile int updateStrategy; - - public LwM2mFwSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { - this.handler = handler; - this.lwM2MClient = lwM2MClient; - this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); - this.type = type; - this.stateUpdate = null; - this.updateStrategy = updateStrategy; - this.initPathId(); - } - - private void initPathId() { - if (FIRMWARE.equals(this.type)) { - this.pathPackageId = LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? - FW_PACKAGE_5_ID : LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? - FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; - this.pathStateId = FW_STATE_ID; - this.pathResultId = FW_RESULT_ID; - this.pathNameId = FW_NAME_ID; - this.pathVerId = FW_5_VER_ID; - this.pathInstallId = FW_UPDATE_ID; - this.wUpdate = FW_UPDATE; - } else if (SOFTWARE.equals(this.type)) { - this.pathPackageId = SW_PACKAGE_ID; - this.pathStateId = SW_UPDATE_STATE_ID; - this.pathResultId = SW_RESULT_ID; - this.pathNameId = SW_NAME_ID; - this.pathVerId = SW_VER_ID; - this.pathInstallId = SW_INSTALL_ID; - this.pathUnInstallId = SW_UN_INSTALL_ID; - this.wUpdate = SW_UPDATE; - } - } - - public void initReadValue(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, String pathIdVer) { - if (pathIdVer != null) { - this.pendingInfoRequestsStart.remove(pathIdVer); - } - if (this.pendingInfoRequestsStart.size() == 0) { - this.infoFwSwUpdate = false; -// if (!FAILED.name().equals(this.stateUpdate)) { - boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) : - this.conditionalSwUpdateStart(handler); - if (conditionalStart) { - this.writeFwSwWare(handler, request); - } -// } - } - } - - /** - * Send FsSw to Lwm2mClient: - * before operation Write: fw_state = DOWNLOADING - */ - public void writeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { - if (this.currentId != null) { - this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null); - String targetIdVer = convertObjectIdToVersionedId(this.pathPackageId, this.lwM2MClient.getRegistration()); - String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO, - LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId); - handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); - log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); - if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { - int chunkSize = 0; - int chunk = 0; - byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); - - TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { - String apiFont = "coap://176.36.143.9:5685"; - String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); - log.warn("89) coapUri: [{}]", uri); - //TODO: user this.rpcRequest??? - TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { - - } - } else { - String msgError = "FirmWareId is null."; - log.warn("6) [{}]", msgError); -// if (this.rpcRequest != null) { -// TODO: refactor. -// handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); -// } - log.error(msgError); - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_ERROR, msgError); - } - } - - public void sendLogs(DefaultLwM2MUplinkMsgHandler handler, String typeOper, String typeInfo, String msgError) { -// this.sendSateOnThingsBoard(handler); - String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", - typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); - if (LOG_LWM2M_ERROR.equals(typeInfo)) { - msg = String.format("%s Error: %s", msg, msgError); - } - handler.logToTelemetry(lwM2MClient, msg); - } - - - /** - * After inspection Update Result - * fw_state/sw_state = UPDATING - * send execute - */ - public void executeFwSwWare(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); - //TODO: user this.rpcRequest??? - TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(pathInstallId).timeout(handler.config.getTimeout()).build(); - request.sendExecuteRequest(lwM2MClient, downlink, new TbLwM2MExecuteCallback(handler, lwM2MClient, pathInstallId)); - } - - /** - * Firmware start: Check if the version has changed and launch a new update. - * -ObjectId 5, Binary or ObjectId 5, URI - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. - * - We launch the update regardless of the state of the firmware and its version. - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. - * * ObjectId 5, Binary - * -- 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. - * Compare current versions by equals. - * * ObjectId 5, URI - * -- 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. - * Compare current versions by contains. - */ - private boolean conditionalFwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { - Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId); - String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId); - String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID); - // #1/#2 - String fwMsg = null; - if ((this.currentVersion != null && ( - ver5 != null && ver5.equals(this.currentVersion) || - ver3 != null && ver3.contains(this.currentVersion) - )) || - (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { - fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR, - this.currentVersion); - } else if (updateResultFw != null && updateResultFw > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { - fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR, - updateResultFw); - } - if (fwMsg != null) { - handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); - return false; - } else { - return true; - } - } - - - /** - * Before operation Execute inspection Update Result : - * 0 - Initial value - */ - public boolean conditionalFwExecuteStart() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return UpdateResultFw.INITIAL.code == updateResult; - } - - /** - * After operation Execute success inspection Update Result : - * 1 - "Firmware updated successfully" - */ - public boolean conditionalFwExecuteAfterSuccess() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; - } - - /** - * After operation Execute success inspection Update Result : - * > 1 error: "Firmware updated successfully" - */ - public boolean conditionalFwExecuteAfterError() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; - } - - /** - * Software start - * - If Update Result -errors (equal or more than 50) - This means that the previous. the update failed. - * * - We launch the update regardless of the state of the firmware and its version. - * - If Update Result is not errors (less than 50) and ver is not empty - This means that before. the update has passed. - * - If Update Result is not errors and ver is empty - This means that there was no update yet or before. UnInstall update - * - If Update Result is not errors and ver is not empty - This means that before unInstall update - * * - Check if the version has changed and launch a new update. - */ - private boolean conditionalSwUpdateStart(DefaultLwM2MUplinkMsgHandler handler) { - Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - // #1/#2 - return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || - ( - (updateResultSw <= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code - ) && - ( - (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) || - (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId))) - ) - ); - } - - /** - * Before operation Execute inspection Update Result : - * 3 - Successfully Downloaded and package integrity verified - */ - public boolean conditionalSwUpdateExecute() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_DOWNLOADED_VERIFIED.code == updateResult; - } - - /** - * After finish operation Execute (success): - * -- inspection Update Result: - * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.") - * -- fw_state/sw_state = UPDATED - *

- * After finish operation Execute (error): - * -- inspection updateResult and send to thingsboard info about error - * --- send to telemetry ( key - this is name Update Result in model) ( - * -- fw_state/sw_state = FAILED - */ - public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - String value = FIRMWARE.equals(this.type) ? UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : - LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); - if (success) { - this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_INFO, null); - } else { - this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); - this.sendLogs(handler, EXECUTE.name(), LOG_LWM2M_ERROR, value); - } - handler.helper.sendParametersOnThingsboardTelemetry( - handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession()); - } - - /** - * After operation Execute success inspection Update Result : - * 2 - "Software successfully installed." - */ - public boolean conditionalSwExecuteAfterSuccess() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult; - } - - /** - * After operation Execute success inspection Update Result : - * >= 50 - error "NOT_ENOUGH_STORAGE" - */ - public boolean conditionalSwExecuteAfterError() { - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; - } - -// private void observeStateUpdate(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request) { -// request.sendAllRequest(lwM2MClient, -// convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, -// null, null, 0, null); -// request.sendAllRequest(lwM2MClient, -// convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, -// null, null, 0, null); -// } - - public void sendSateOnThingsBoard(DefaultLwM2MUplinkMsgHandler handler) { - if (StringUtils.trimToNull(this.stateUpdate) != null) { - List result = new ArrayList<>(); - TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE)); - kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate); - result.add(kvProto.build()); - handler.helper.sendParametersOnThingsboardTelemetry(result, - handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration())); - } - } - - public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) { - this.infoFwSwUpdate = true; - this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( - this.pathStateId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( - this.pathResultId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( - FW_3_VER_ID, this.lwM2MClient.getRegistration())); - if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || - LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || - SOFTWARE.equals(this.type)) { - this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( - this.pathVerId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( - this.pathNameId, this.lwM2MClient.getRegistration())); - } - this.pendingInfoRequestsStart.forEach(versionedId -> { - TbLwM2MObserveRequest downlink = TbLwM2MObserveRequest.builder().versionedId(versionedId).build(); - request.sendObserveRequest(this.lwM2MClient, downlink, new TbLwM2MObserveCallback(handler, lwM2MClient, versionedId)); - }); - - } - - /** - * Before operation Execute (FwUpdate) inspection Update Result : - * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED - * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value - * - start Execute (FwUpdate) - * After finished operation Execute (FwUpdate) inspection Update Result : - * - after start operation Execute (FwUpdate): fw_state = UPDATING - * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") - * - finished operation Execute (FwUpdate) - */ - public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, - Registration registration, String path, int value) { - if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { - if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { - if (DOWNLOADED.name().equals(this.getStateUpdate()) - && this.conditionalFwExecuteStart()) { - this.executeFwSwWare(handler, request); - } else if (UPDATING.name().equals(this.getStateUpdate()) - && this.conditionalFwExecuteAfterSuccess()) { - this.finishFwSwUpdate(handler, true); - } else if (UPDATING.name().equals(this.getStateUpdate()) - && this.conditionalFwExecuteAfterError()) { - this.finishFwSwUpdate(handler, false); - } - } - } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { - if (this.currentId != null && (convertObjectIdToVersionedId(FW_STATE_ID, registration).equals(path))) { - String state = toOtaPackageUpdateStatus(UpdateStateFw.fromStateFwByCode(value)).name(); - if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { - this.stateUpdate = state; - this.sendSateOnThingsBoard(handler); - } - if (value == UpdateStateFw.DOWNLOADED.code) { - this.executeFwSwWare(handler, request); - } - handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value); - } - if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { - if (this.currentId != null && value == UpdateResultFw.INITIAL.code) { - this.setStateUpdate(INITIATED.name()); - } else if (this.currentId != null && value == UpdateResultFw.UPDATE_SUCCESSFULLY.code) { - this.setStateUpdate(UPDATED.name()); - } else if (value > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { - this.setStateUpdate(FAILED.name()); - } - this.sendSateOnThingsBoard(handler); - } - } else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) { - - } - } -} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/common/LwM2MExecutorAwareService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/common/LwM2MExecutorAwareService.java new file mode 100644 index 0000000000..1f1dff784f --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/common/LwM2MExecutorAwareService.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.common; + +import org.thingsboard.common.util.ThingsBoardExecutors; + +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; + +public abstract class LwM2MExecutorAwareService { + + protected ExecutorService executor; + + protected abstract int getExecutorSize(); + + protected abstract String getExecutorName(); + + protected void init() { + this.executor = ThingsBoardExecutors.newWorkStealingPool(getExecutorSize(), getExecutorName()); + } + + public void destroy() { + if (executor != null) { + executor.shutdownNow(); + } + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java index 764d5ff5ef..561b103277 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/AbstractTbLwM2MRequestCallback.java @@ -17,30 +17,30 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; @Slf4j public abstract class AbstractTbLwM2MRequestCallback implements DownlinkRequestCallback { - protected final LwM2mUplinkMsgHandler handler; + protected final LwM2MTelemetryLogService logService; protected final LwM2mClient client; - protected AbstractTbLwM2MRequestCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { - this.handler = handler; + protected AbstractTbLwM2MRequestCallback(LwM2MTelemetryLogService logService, LwM2mClient client) { + this.logService = logService; this.client = client; } @Override public void onValidationError(String params, String msg) { log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg); - handler.logToTelemetry(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg)); + logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg)); } @Override public void onError(String params, Exception e) { log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e); - handler.logToTelemetry(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e)); + logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index da513a1934..52ef83bf8e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -26,7 +26,6 @@ import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.observation.Observation; -import org.eclipse.leshan.core.request.CancelObservationRequest; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.request.DiscoverRequest; @@ -45,7 +44,6 @@ import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; @@ -54,17 +52,18 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -74,25 +73,38 @@ import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; import static org.eclipse.leshan.core.attributes.Attribute.STEP; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; @Slf4j @Service @TbLwM2mTransportComponent @RequiredArgsConstructor -public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { - private ExecutorService responseRequestExecutor; +public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService implements LwM2mDownlinkMsgHandler { public LwM2mValueConverterImpl converter; private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; + private final LwM2MTelemetryLogService logService; @PostConstruct public void init() { + super.init(); this.converter = LwM2mValueConverterImpl.getInstance(); - responseRequestExecutor = Executors.newFixedThreadPool(this.config.getResponsePoolSize(), - new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); + } + + @PreDestroy + public void destroy() { + super.destroy(); + } + + @Override + protected int getExecutorSize() { + return config.getDownlinkPoolSize(); + } + + @Override + protected String getExecutorName() { + return "LwM2M Downlink"; } @Override @@ -221,7 +233,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { **/ Collection resources = client.getNewResourceForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); - WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, convertResourceModelTypeToContentFormat(client, resourceModelWrite.type), resultIds.getObjectId(), + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); sendRequest(client, downlink, request.getTimeout(), callback); } else if (resultIds.isObjectInstance()) { @@ -245,19 +258,24 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { private , T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback callback) { Registration registration = client.getRegistration(); - context.getServer().send(registration, request, timeoutInMs, response -> { - responseRequestExecutor.submit(() -> { - try { - callback.onSuccess(request, response); - } catch (Exception e) { - log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); - } + try { + logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPath())); + context.getServer().send(registration, request, timeoutInMs, response -> { + executor.submit(() -> { + try { + callback.onSuccess(request, response); + } catch (Exception e) { + log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); + } + }); + }, e -> { + executor.submit(() -> { + callback.onError(JacksonUtil.toString(request), e); + }); }); - }, e -> { - responseRequestExecutor.submit(() -> { - callback.onError(JacksonUtil.toString(request), e); - }); - }); + } catch (Exception e) { + callback.onError(JacksonUtil.toString(request), e); + } } private WriteRequest getWriteRequestSingleResource(ResourceModel.Type type, ContentFormat contentFormat, int objectId, int instanceId, int resourceId, Object value) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java index 42f2a1baec..f90e0f8b97 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelAllObserveCallback.java @@ -17,21 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; @Slf4j public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback { - public TbLwM2MCancelAllObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client) { - super(handler, client); + public TbLwM2MCancelAllObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client) { + super(logService, client); } @Override public void onSuccess(TbLwM2MCancelAllRequest request, Integer canceledSubscriptionsCount) { log.trace("[{}] Cancel of all observations was successful: {}", client.getEndpoint(), canceledSubscriptionsCount); - handler.logToTelemetry(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount)); + logService.log(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java index eec33963d5..311be03462 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MCancelObserveCallback.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -27,15 +28,15 @@ public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback private final String versionedId; - public TbLwM2MCancelObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { - super(handler, client); + public TbLwM2MCancelObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { + super(logService, client); this.versionedId = versionedId; } @Override public void onSuccess(TbLwM2MCancelObserveRequest request, Integer canceledSubscriptionsCount) { log.trace("[{}] Cancel observation of [{}] successful: {}", client.getEndpoint(), versionedId, canceledSubscriptionsCount); - handler.logToTelemetry(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount)); + logService.log(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java index 32c82404c6..4c0a758572 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDeleteCallback.java @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.request.DeleteRequest; import org.eclipse.leshan.core.response.DeleteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; public class TbLwM2MDeleteCallback extends TbLwM2MTargetedCallback { - public TbLwM2MDeleteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MDeleteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(logService, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java index 5ae20e064d..41172f0599 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MDiscoverCallback.java @@ -17,13 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.request.DiscoverRequest; import org.eclipse.leshan.core.response.DiscoverResponse; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MDiscoverCallback extends TbLwM2MTargetedCallback { - public TbLwM2MDiscoverCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MDiscoverCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(logService, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java index d2aed6a4fd..992cf6d616 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MExecuteCallback.java @@ -17,13 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.response.ExecuteResponse; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; public class TbLwM2MExecuteCallback extends TbLwM2MTargetedCallback { - public TbLwM2MExecuteCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MExecuteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(logService, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java index 7fca29aa8c..dde49c870d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MObserveCallback.java @@ -18,14 +18,15 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.response.ObserveResponse; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @Slf4j -public class TbLwM2MObserveCallback extends TbLwM2MTargetedCallback { +public class TbLwM2MObserveCallback extends TbLwM2MUplinkTargetedCallback { - public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(handler, logService, client, targetId); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java index 657fd04fd0..59247b103b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MReadCallback.java @@ -19,13 +19,14 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; @Slf4j -public class TbLwM2MReadCallback extends TbLwM2MTargetedCallback { +public class TbLwM2MReadCallback extends TbLwM2MUplinkTargetedCallback { - public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(handler, logService, client, targetId); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java index 258bec53ac..373c882ec4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MTargetedCallback.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; @@ -26,8 +27,8 @@ public abstract class TbLwM2MTargetedCallback extends AbstractTbLwM2MReque protected final String versionedId; - public TbLwM2MTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String versionedId) { - super(handler, client); + public TbLwM2MTargetedCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { + super(logService, client); this.versionedId = versionedId; } @@ -36,7 +37,7 @@ public abstract class TbLwM2MTargetedCallback extends AbstractTbLwM2MReque //TODO convert camelCase to "camel case" using .split("(? extends TbLwM2MTargetedCallback { + + protected LwM2mUplinkMsgHandler handler; + + public TbLwM2MUplinkTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { + super(logService, client, versionedId); + this.handler = handler; + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java index 372534fa7a..e346ad965a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteAttributesCallback.java @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; public class TbLwM2MWriteAttributesCallback extends TbLwM2MTargetedCallback { - public TbLwM2MWriteAttributesCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MWriteAttributesCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(logService, client, targetId); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index 89586bd113..4746077f19 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -18,12 +18,13 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -public class TbLwM2MWriteResponseCallback extends TbLwM2MTargetedCallback { +public class TbLwM2MWriteResponseCallback extends TbLwM2MUplinkTargetedCallback { - public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { - super(handler, client, targetId); + public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { + super(handler, logService, client, targetId); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java new file mode 100644 index 0000000000..421f4cf3a6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/DefaultLwM2MTelemetryLogService.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.log; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; + +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; + +@Slf4j +@Service +@TbLwM2mTransportComponent +@RequiredArgsConstructor +public class DefaultLwM2MTelemetryLogService implements LwM2MTelemetryLogService { + + private final LwM2mClientContext clientContext; + private final LwM2mTransportServerHelper helper; + + /** + * @param logMsg - text msg + * @param registrationId - Id of Registration LwM2M Client + */ + @Override + public void log(String registrationId, String logMsg) { + log(clientContext.getClientByRegistrationId(registrationId), logMsg); + } + + @Override + public void log(LwM2mClient client, String logMsg) { + if (logMsg != null && client != null && client.getSession() != null) { + if (logMsg.length() > 1024) { + logMsg = logMsg.substring(0, 1024); + } + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession()); + } + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/LwM2MTelemetryLogService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/LwM2MTelemetryLogService.java new file mode 100644 index 0000000000..ff8543303e --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/log/LwM2MTelemetryLogService.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.log; + +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; + +public interface LwM2MTelemetryLogService { + + void log(LwM2mClient client, String msg); + + void log(String registrationId, String msg); + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 3f86131320..d44770e591 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.DonAsynchron; -import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageKey; @@ -41,33 +40,35 @@ import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j @Service @TbLwM2mTransportComponent @RequiredArgsConstructor -public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { +public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService implements LwM2MOtaUpdateService { public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION); public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); @@ -93,8 +94,8 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { private final LwM2mUplinkMsgHandler uplinkHandler; private final LwM2mDownlinkMsgHandler downlinkHandler; private final OtaPackageDataCache otaPackageDataCache; + private final LwM2MTelemetryLogService logService; private final LwM2mTransportServerHelper helper; - private ExecutorService executor; @Autowired @Lazy @@ -102,8 +103,22 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { @PostConstruct public void init() { - //TODO: define parallelism in constant - executor = ThingsBoardExecutors.newWorkStealingPool(4, "LwM2M OTA Updates"); + super.init(); + } + + @PreDestroy + public void destroy() { + super.destroy(); + } + + @Override + protected int getExecutorSize() { + return config.getOtaPoolSize(); + } + + @Override + protected String getExecutorName() { + return "LwM2M OTA"; } @Override @@ -112,7 +127,6 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { //TODO: check that the client supports FW and SW by checking the supported objects in the model. List attributesToFetch = new ArrayList<>(); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - fwInfo.setSupported(client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)); if (fwInfo.isSupported()) { attributesToFetch.add(FIRMWARE_TITLE); attributesToFetch.add(FIRMWARE_VERSION); @@ -138,6 +152,14 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { } } + @Override + public void forceFirmwareUpdate(LwM2mClient client) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setRetryAttempts(0); + fwInfo.setFailedPackageId(null); + startFirmwareUpdateIfNeeded(client, fwInfo); + } + @Override public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl) { LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); @@ -147,33 +169,48 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { @Override public void onCurrentFirmwareNameUpdate(LwM2mClient client, String name) { + log.debug("[{}] Current fw name: {}", client.getEndpoint(), name); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); fwInfo.setCurrentName(name); } @Override - public void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version) { + public void onCurrentFirmwareVersion3Update(LwM2mClient client, String version) { + log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setCurrentVersion3(version); + } + + @Override + public void onCurrentFirmwareVersion5Update(LwM2mClient client, String version) { + log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - fwInfo.setCurrentVersion(version); + fwInfo.setCurrentVersion5(version); } @Override - public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state) { + public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long stateCode) { + log.debug("[{}] Current fw state: {}", client.getEndpoint(), stateCode); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - UpdateStateFw newState = UpdateStateFw.fromStateFwByCode(state.intValue()); - if (UpdateStateFw.DOWNLOADED.equals(newState)) { + UpdateStateFw state = UpdateStateFw.fromStateFwByCode(stateCode.intValue()); + if (UpdateStateFw.DOWNLOADED.equals(state)) { executeFwUpdate(client); } - fwInfo.setUpdateState(newState); - sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(newState)); + fwInfo.setUpdateState(state); + Optional status = LwM2mTransportUtil.toOtaPackageUpdateStatus(state); + status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, + otaStatus, "Firmware Update State: " + state.name())); } @Override public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { + log.debug("[{}] Current fw result: {}", client.getEndpoint(), code); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); - sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(result)); - if (result.isAgain && fwInfo.getRetryAttempts() <= 2) { + Optional status = LwM2mTransportUtil.toOtaPackageUpdateStatus(result); + status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, + otaStatus, "Firmware Update Result: " + result.name())); + if (result.isAgain() && fwInfo.getRetryAttempts() <= 2) { fwInfo.setRetryAttempts(fwInfo.getRetryAttempts() + 1); startFirmwareUpdateIfNeeded(client, fwInfo); } else { @@ -183,6 +220,7 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { @Override public void onCurrentFirmwareDeliveryMethodUpdate(LwM2mClient client, Long value) { + log.debug("[{}] Current fw delivery method: {}", client.getEndpoint(), value); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); fwInfo.setDeliveryMethod(value.intValue()); } @@ -193,22 +231,29 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { } private void startFirmwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { - if (fwInfo.isUpdateRequired()) { - if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { - log.info("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); - startFirmwareUpdateUsingUrl(client, fwInfo.getTargetUrl()); - } else { - startFirmwareUpdateUsingBinary(client, fwInfo); + try { + if (!fwInfo.isSupported()) { + log.debug("[{}] Fw update is not supported: {}", client.getEndpoint(), fwInfo); + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Client does not support firmware update or profile misconfiguration!"); + } else if (fwInfo.isUpdateRequired()) { + if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { + log.debug("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); + startFirmwareUpdateUsingUrl(client, fwInfo.getTargetUrl()); + } else { + log.debug("[{}] Starting update to [{}{}] using binary", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion()); + startFirmwareUpdateUsingBinary(client, fwInfo); + } } - } else if (fwInfo.isUpdateFailed()) { - sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED); + } catch (Exception e) { + log.warn("[{}] failed to update client: {}", client.getEndpoint(), fwInfo, e); + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Internal server error: " + e.getMessage()); } } private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); - TbLwM2MWriteUpdateRequest request = TbLwM2MWriteUpdateRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); - downlinkHandler.sendWriteUpdateRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, client, targetIdVer)); + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, targetIdVer)); } public void startFirmwareUpdateUsingBinary(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { @@ -217,41 +262,44 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { new TransportServiceCallback<>() { @Override public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(OtaPackageType.FIRMWARE.name())) { - UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); - LwM2MFirmwareUpdateStrategy strategy; - if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { - strategy = fwInfo.getStrategy(); - } else { - strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; - } - switch (strategy) { - case OBJ_5_BINARY: - byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); - TbLwM2MWriteReplaceRequest writeRequest = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) - .value(firmwareChunk).contentFormat(ContentFormat.OPAQUE) - .timeout(config.getTimeout()).build(); - downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId)); - break; - case OBJ_5_TEMP_URL: - startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString()); - break; - default: - //TODO: send log to telemetry - } - } else { - //TODO: send log to telemetry - } + executor.submit(() -> doUpdateFirmwareUsingBinary(response, fwInfo, versionedId, client)); } @Override public void onError(Throwable e) { - log.trace("Failed to process firmwareUpdate ", e); + logService.log(client, "Failed to process firmware update: " + e.getMessage()); } }); } + private void doUpdateFirmwareUsingBinary(TransportProtos.GetOtaPackageResponseMsg response, LwM2MClientOtaInfo fwInfo, String versionedId, LwM2mClient client) { + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { + UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); + LwM2MFirmwareUpdateStrategy strategy; + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { + strategy = fwInfo.getStrategy(); + } else { + strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; + } + switch (strategy) { + case OBJ_5_BINARY: + byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); + TbLwM2MWriteReplaceRequest writeRequest = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) + .value(firmwareChunk).contentFormat(ContentFormat.OPAQUE) + .timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId)); + break; + case OBJ_5_TEMP_URL: + startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString()); + break; + default: + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Unsupported strategy: " + strategy.name()); + } + } else { + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Failed to fetch OTA package: " + response.getResponseStatus()); + } + } + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(TransportProtos.SessionInfoProto sessionInfo, String nameFwSW) { return TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) @@ -264,7 +312,7 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { private void executeFwUpdate(LwM2mClient client) { TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(FW_EXECUTE_ID).timeout(config.getTimeout()).build(); - downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(uplinkHandler, client, FW_EXECUTE_ID)); + downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, FW_EXECUTE_ID)); } private Optional getAttributeValue(List attrs, String keyName) { @@ -298,11 +346,14 @@ public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { } - private void sendStateUpdateToTelemetry(LwM2mClient client, LwM2MClientOtaInfo fwInfo, OtaPackageUpdateStatus status) { + private void sendStateUpdateToTelemetry(LwM2mClient client, LwM2MClientOtaInfo fwInfo, OtaPackageUpdateStatus status, String log) { List result = new ArrayList<>(); TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(fwInfo.getType(), STATE)); kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(status.name()); result.add(kvProto.build()); + kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(LOG_LWM2M_TELEMETRY); + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(log); + result.add(kvProto.build()); helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 50b44f8d06..43d4f4acf5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -31,7 +31,6 @@ public class LwM2MClientOtaInfo { private final OtaPackageType type; private String baseUrl; - private boolean supported; private boolean targetFetchFailure; private String targetName; @@ -40,7 +39,8 @@ public class LwM2MClientOtaInfo { private boolean currentFetchFailure; private String currentName; - private String currentVersion; + private String currentVersion3; + private String currentVersion5; private Integer deliveryMethod; //TODO: use value from device if applicable; @@ -65,24 +65,27 @@ public class LwM2MClientOtaInfo { } public boolean isUpdateRequired() { - if (StringUtils.isEmpty(targetName) || - StringUtils.isEmpty(targetVersion) || - (StringUtils.isEmpty(currentName) && StringUtils.isEmpty(currentVersion))) { + if (StringUtils.isEmpty(targetName) || StringUtils.isEmpty(targetVersion) || !isSupported()) { return false; } else { String targetPackageId = getPackageId(targetName, targetVersion); - String currentPackageId = getPackageId(currentName, currentVersion); + String currentPackageIdUsingObject5 = getPackageId(currentName, currentVersion5); if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { return false; } else { - return !targetPackageId.equals(currentPackageId); + if (targetPackageId.equals(currentPackageIdUsingObject5)) { + return false; + } else if (StringUtils.isNotEmpty(currentVersion3)) { + return !currentVersion3.contains(targetPackageId); + } else { + return true; + } } } } - public boolean isUpdateFailed() { - return (StringUtils.isNotEmpty(targetName) && StringUtils.isNotEmpty(targetVersion) && - (StringUtils.isEmpty(currentName) && StringUtils.isEmpty(currentVersion))); + public boolean isSupported() { + return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3); } public void setUpdateResult(UpdateResultFw updateResult) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index 6386e94364..9c7905a5e8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -23,13 +23,17 @@ public interface LwM2MOtaUpdateService { void init(LwM2mClient client); + void forceFirmwareUpdate(LwM2mClient client); + void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl); void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion); void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); - void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version); + void onCurrentFirmwareVersion3Update(LwM2mClient client, String version); + + void onCurrentFirmwareVersion5Update(LwM2mClient client, String version); void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 533458ce76..5de223fa2f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -50,6 +50,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Map; @@ -69,6 +70,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private final LwM2MTransportServerConfig config; private final LwM2mUplinkMsgHandler uplinkHandler; private final LwM2mDownlinkMsgHandler downlinkHandler; + private final LwM2MTelemetryLogService logService; private final Map rpcSubscriptions = new ConcurrentHashMap<>(); @Override @@ -148,14 +150,14 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MReadCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MReadCallback(uplinkHandler, logService, client, versionedId); var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); downlinkHandler.sendReadRequest(client, request, rpcCallback); } private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, logService, client, versionedId); var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); downlinkHandler.sendObserveRequest(client, request, rpcCallback); } @@ -172,14 +174,14 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private void sendDiscoverRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MDiscoverCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MDiscoverCallback(logService, client, versionedId); var rpcCallback = new RpcDiscoverCallback(transportService, client, requestMsg, mainCallback); downlinkHandler.sendDiscoverRequest(client, request, rpcCallback); } private void sendExecuteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MExecuteCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MExecuteCallback(logService, client, versionedId); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); downlinkHandler.sendExecuteRequest(client, downlink, rpcCallback); } @@ -189,7 +191,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId) .attributes(requestBody.getAttributes()) .timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MWriteAttributesCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MWriteAttributesCallback(logService, client, versionedId); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); downlinkHandler.sendWriteAttributesRequest(client, request, rpcCallback); } @@ -198,7 +200,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); TbLwM2MWriteUpdateRequest.TbLwM2MWriteUpdateRequestBuilder builder = TbLwM2MWriteUpdateRequest.builder().versionedId(versionedId); builder.value(requestBody.getValue()).timeout(this.config.getTimeout()); - var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); downlinkHandler.sendWriteUpdateRequest(client, builder.build(), rpcCallback); } @@ -208,28 +210,28 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) .value(requestBody.getValue()) .timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); } private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MCancelObserveCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId); var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); downlinkHandler.sendCancelObserveRequest(client, downlink, rpcCallback); } private void sendDeleteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { TbLwM2MDeleteRequest downlink = TbLwM2MDeleteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MDeleteCallback(uplinkHandler, client, versionedId); + var mainCallback = new TbLwM2MDeleteCallback(logService, client, versionedId); var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); downlinkHandler.sendDeleteRequest(client, downlink, rpcCallback); } private void sendCancelAllObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { TbLwM2MCancelAllRequest downlink = TbLwM2MCancelAllRequest.builder().timeout(this.config.getTimeout()).build(); - var mainCallback = new TbLwM2MCancelAllObserveCallback(uplinkHandler, client); + var mainCallback = new TbLwM2MCancelAllObserveCallback(logService, client); var rpcCallback = new RpcCancelAllObserveCallback(transportService, client, requestMsg, mainCallback); downlinkHandler.sendCancelAllRequest(client, downlink, rpcCallback); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 61c4b077d9..5b61ffc768 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -44,8 +44,6 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -66,9 +64,9 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; @@ -82,12 +80,14 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallbac import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -105,9 +105,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_DELIVERY_METHOD; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; @@ -115,22 +113,17 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @Slf4j @Service @TbLwM2mTransportComponent -public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { +public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService implements LwM2mUplinkMsgHandler { - private ExecutorService registrationExecutor; - private ExecutorService updateRegistrationExecutor; - private ExecutorService unRegistrationExecutor; public LwM2mValueConverterImpl converter; private final TransportService transportService; @@ -138,6 +131,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private final LwM2MAttributesService attributesService; private final LwM2MOtaUpdateService otaService; public final LwM2MTransportServerConfig config; + private final LwM2MTelemetryLogService logService; public final OtaPackageDataCache otaPackageDataCache; public final LwM2mTransportServerHelper helper; private final TbLwM2MDtlsSessionStore sessionStore; @@ -148,8 +142,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public final Map firmwareUpdateState; public DefaultLwM2MUplinkMsgHandler(TransportService transportService, - LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, + LwM2MTransportServerConfig config, + LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, + LwM2MTelemetryLogService logService, @Lazy LwM2MOtaUpdateService otaService, @Lazy LwM2MAttributesService attributesService, @Lazy LwM2MRpcRequestHandler rpcHandler, @@ -162,6 +158,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.config = config; this.helper = helper; this.clientContext = clientContext; + this.logService = logService; this.rpcHandler = rpcHandler; this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; this.otaPackageDataCache = otaPackageDataCache; @@ -171,14 +168,27 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } @PostConstruct - public void initAttributes() { + public void init() { + super.init(); this.context.getScheduler().scheduleAtFixedRate(this::reportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS); - this.registrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getRegisteredPoolSize(), "LwM2M registration"); - this.updateRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUpdateRegisteredPoolSize(), "LwM2M update registration"); - this.unRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUnRegisteredPoolSize(), "LwM2M unRegistration"); this.converter = LwM2mValueConverterImpl.getInstance(); } + @PreDestroy + public void destroy() { + super.destroy(); + } + + @Override + protected String getExecutorName() { + return "LwM2M uplink"; + } + + @Override + protected int getExecutorSize() { + return config.getUplinkPoolSize(); + } + /** * Start registration device * Create session: Map, LwM2MClient> @@ -194,7 +204,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param previousObservations - may be null */ public void onRegistered(Registration registration, Collection previousObservations) { - registrationExecutor.submit(() -> { + executor.submit(() -> { LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); try { log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); @@ -204,7 +214,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.info("[{}] Closing old session: {}", registration.getEndpoint(), new UUID(oldSessionInfo.get().getSessionIdMSB(), oldSessionInfo.get().getSessionIdLSB())); closeSession(oldSessionInfo.get()); } - this.logToTelemetry(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); + logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); SessionInfoProto sessionInfo = lwM2MClient.getSession(); transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); @@ -215,8 +225,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) .build(); transportService.process(msg, null); - this.getInfoFirmwareUpdate(lwM2MClient); - this.getInfoSoftwareUpdate(lwM2MClient); this.initClientTelemetry(lwM2MClient); this.initAttributes(lwM2MClient); otaService.init(lwM2MClient); @@ -229,11 +237,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { // Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again. onRegistered(registration, previousObservations); } else { - this.logToTelemetry(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); + logService.log(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); } } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); - this.logToTelemetry(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to: " + t.getMessage()); + logService.log(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to: " + t.getMessage()); } }); } @@ -244,7 +252,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param registration - Registration LwM2M Client */ public void updatedReg(Registration registration) { - updateRegistrationExecutor.submit(() -> { + executor.submit(() -> { LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); try { log.warn("[{}] [{{}] Client: update after Registration", registration.getEndpoint(), registration.getId()); @@ -265,7 +273,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); - this.logToTelemetry(lwM2MClient, LOG_LWM2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); + logService.log(lwM2MClient, LOG_LWM2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); } }); } @@ -275,10 +283,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { - unRegistrationExecutor.submit(() -> { + executor.submit(() -> { LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); try { - this.logToTelemetry(client, LOG_LWM2M_INFO + ": Client unRegistration"); + logService.log(client, LOG_LWM2M_INFO + ": Client unRegistration"); clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { @@ -292,7 +300,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); - this.logToTelemetry(client, LOG_LWM2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); + logService.log(client, LOG_LWM2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); } }); } @@ -305,7 +313,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { @Override public void onSleepingDev(Registration registration) { log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); - this.logToTelemetry(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is sleeping!"); + logService.log(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is sleeping!"); //TODO: associate endpointId with device information. } @@ -403,29 +411,10 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { @Override public void onAwakeDev(Registration registration) { log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); - this.logToTelemetry(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is awake!"); + logService.log(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is awake!"); //TODO: associate endpointId with device information. } - /** - * @param logMsg - text msg - * @param registrationId - Id of Registration LwM2M Client - */ - @Override - public void logToTelemetry(String registrationId, String logMsg) { - logToTelemetry(clientContext.getClientByRegistrationId(registrationId), logMsg); - } - - @Override - public void logToTelemetry(LwM2mClient client, String logMsg) { - if (logMsg != null && client != null && client.getSession() != null) { - if (logMsg.length() > 1024) { - logMsg = logMsg.substring(0, 1024); - } - this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession()); - } - } - /** * #1 clientOnlyObserveAfterConnect == true * - Only Observe Request to the client marked as observe from the profile configuration. @@ -441,10 +430,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId()); Set supportedObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); if (supportedObjects != null && supportedObjects.size() > 0) { - if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == profile.getClientLwM2mSettings().getClientOnlyObserveAfterConnect()) { - // #2 - supportedObjects.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId)); - } // #1 this.sendReadRequests(lwM2MClient, profile, supportedObjects); this.sendObserveRequests(lwM2MClient, profile, supportedObjects); @@ -461,7 +446,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { CountDownLatch latch = new CountDownLatch(targetIds.size()); targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId, - new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)))); + new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)))); try { latch.await(); } catch (InterruptedException e) { @@ -475,7 +460,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { CountDownLatch latch = new CountDownLatch(targetIds.size()); targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, - new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, lwM2MClient, targetId)))); + new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); try { latch.await(); } catch (InterruptedException e) { @@ -501,11 +486,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void sendDiscoverRequest(LwM2mClient lwM2MClient, String targetId) { TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(targetId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, request, new TbLwM2MDiscoverCallback(this, lwM2MClient, targetId)); + defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, request, new TbLwM2MDiscoverCallback(logService, lwM2MClient, targetId)); } private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { - sendReadRequest(lwM2MClient, versionedId, new TbLwM2MReadCallback(this, lwM2MClient, versionedId)); + sendReadRequest(lwM2MClient, versionedId, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)); } private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { @@ -514,7 +499,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { - sendObserveRequest(lwM2MClient, versionedId, new TbLwM2MObserveCallback(this, lwM2MClient, versionedId)); + sendObserveRequest(lwM2MClient, versionedId, new TbLwM2MObserveCallback(this, logService, lwM2MClient, versionedId)); } private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback callback) { @@ -524,12 +509,12 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(targetId).attributes(params).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, request, new TbLwM2MWriteAttributesCallback(this, lwM2MClient, targetId)); + defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, request, new TbLwM2MWriteAttributesCallback(logService, lwM2MClient, targetId)); } private void sendCancelObserveRequest(String versionedId, LwM2mClient client) { TbLwM2MCancelObserveRequest request = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(this, client, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(logService, client, versionedId)); } private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer) { @@ -565,9 +550,9 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, registration))) { otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, registration))) { - otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + otaService.onCurrentFirmwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); } else if (path.equals(convertObjectIdToVersionedId(FW_5_VER_ID, registration))) { - otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + otaService.onCurrentFirmwareVersion5Update(lwM2MClient, (String) lwM2mResource.getValue()); } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, registration))) { otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, registration))) { @@ -919,93 +904,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { DonAsynchron.withCallback(attributesService.getSharedAttributes(lwM2MClient, keysToFetch), v -> attributesService.onAttributesUpdate(lwM2MClient, v), t -> log.error("[{}] Failed to get attributes", lwM2MClient.getEndpoint(), t), - registrationExecutor); - } - } - - public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) { - if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) { - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); - if (sessionInfo != null) { - DefaultLwM2MUplinkMsgHandler handler = this; - this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), - new TransportServiceCallback<>() { - @Override - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(OtaPackageType.FIRMWARE.name())) { - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(DefaultLwM2MUplinkMsgHandler.this, clientContext); -// if (rpcRequest != null) { -// fwUpdate.setStateUpdate(INITIATED.name()); -// } - if (!FAILED.name().equals(fwUpdate.getStateUpdate())) { - log.warn("7) firmware start with ver: [{}]", response.getVersion()); -// fwUpdate.setRpcRequest(rpcRequest); - fwUpdate.setCurrentVersion(response.getVersion()); - fwUpdate.setCurrentTitle(response.getTitle()); - fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); -// if (rpcRequest == null) { - fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); -// } else { -// fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); -// } - } else { - String msgError = String.format("OtaPackage device: %s, version: %s, stateUpdate: %s", - lwM2MClient.getDeviceName(), response.getVersion(), fwUpdate.getStateUpdate()); - log.warn("7_1 [{}]", msgError); - } - } else { - String msgError = String.format("OtaPackage device: %s, responseStatus: %s", - lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); - log.trace(msgError); -// if (rpcRequest != null) { - //TODO: refactor -// sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); -// } - } - } - - @Override - public void onError(Throwable e) { - log.trace("Failed to process firmwareUpdate ", e); - } - }); - } - } - } - - public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient) { - if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); - if (sessionInfo != null) { - DefaultLwM2MUplinkMsgHandler handler = this; - transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), - new TransportServiceCallback<>() { - @Override - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(OtaPackageType.SOFTWARE.name())) { -// lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); - lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); - lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); - lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); -// if (rpcRequest == null) { - lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); -// } else { -// lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); -// } - } else { - log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); - } - } - - @Override - public void onError(Throwable e) { - log.trace("Failed to process softwareUpdate ", e); - } - }); - } + executor); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 517db388e4..372daa7052 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -52,10 +52,6 @@ public interface LwM2mUplinkMsgHandler { void onAwakeDev(Registration registration); - void logToTelemetry(LwM2mClient client, String msg); - - void logToTelemetry(String registrationId, String msg); - void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request); void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index d81b28a5ff..d7e97231e0 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -142,12 +142,10 @@ transport: timeout: "${LWM2M_TIMEOUT:120000}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" - response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" - registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" - registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}" + uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" + downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" + ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" - update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" - un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}" # Use redis for Security and Registration stores redis.enabled: "${LWM2M_REDIS_ENABLED:false}" From 8b285c8e77f5ff6fb9b88e68c4acc375fa268b5a Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 15 Jun 2021 12:34:25 +0300 Subject: [PATCH 100/151] created OtaPackageTransportResource --- .../common/msg/session/FeatureType.java | 2 +- .../common/msg/session/SessionMsgType.java | 5 +- .../transport/coap/CoapTransportResource.java | 72 --------- .../transport/coap/CoapTransportService.java | 3 + .../coap/OtaPackageTransportResource.java | 144 ++++++++++++++++++ 5 files changed, 149 insertions(+), 77 deletions(-) create mode 100644 common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java index ae965e327a..9be2957401 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.msg.session; public enum FeatureType { - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java index 939197af80..5dbea04d59 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java @@ -30,10 +30,7 @@ public enum SessionMsgType { SESSION_OPEN, SESSION_CLOSE, - CLAIM_REQUEST(), - - GET_FIRMWARE_REQUEST, - GET_SOFTWARE_REQUEST; + CLAIM_REQUEST(); private final boolean requiresRulesProcessing; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 33f122fa3f..95cdeada88 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/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 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -139,10 +138,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { processExchangeGetRequest(exchange, featureType.get()); } else if (featureType.get() == FeatureType.ATTRIBUTES) { processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); - } else if (featureType.get() == FeatureType.FIRMWARE) { - processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); - } else if (featureType.get() == FeatureType.SOFTWARE) { - processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST); } else { log.trace("Invalid feature type parameter"); exchange.respond(CoAP.ResponseCode.BAD_REQUEST); @@ -349,12 +344,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { coapTransportAdaptor.convertToGetAttributes(sessionId, request), new CoapNoOpCallback(exchange)); break; - case GET_FIRMWARE_REQUEST: - getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.FIRMWARE); - break; - case GET_SOFTWARE_REQUEST: - getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.SOFTWARE); - break; } } catch (AdaptorException e) { log.trace("[{}] Failed to decode message: ", sessionId, e); @@ -366,16 +355,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); } - private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { - TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) - .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); - } - private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { tokenToObserveNotificationSeqMap.remove(token); return tokenToSessionInfoMap.remove(token); @@ -470,57 +449,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private class OtaPackageCallback implements TransportServiceCallback { - private final CoapExchange exchange; - - OtaPackageCallback(CoapExchange exchange) { - this.exchange = exchange; - } - - @Override - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) { - String title = exchange.getQueryParameter("title"); - String version = exchange.getQueryParameter("version"); - if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { - String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); - if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { - String strChunkSize = exchange.getQueryParameter("size"); - String strChunk = exchange.getQueryParameter("chunk"); - int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); - int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); - exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); - } - else if (firmwareId != null) { - sendOtaData(exchange, firmwareId); - } else { - exchange.respond(CoAP.ResponseCode.BAD_REQUEST); - } - } else { - exchange.respond(CoAP.ResponseCode.NOT_FOUND); - } - } - - @Override - public void onError(Throwable e) { - log.warn("Failed to process request", e); - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); - } - } - - private void sendOtaData(CoapExchange exchange, String firmwareId) { - Response response = new Response(CoAP.ResponseCode.CONTENT); - byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId); - if (fwData != null && fwData.length > 0) { - response.setPayload(fwData); - if (exchange.getRequestOptions().getBlock2() != null) { - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean moreFlag = fwData.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, 0); - } - exchange.respond(response); - } - } - private static class CoapSessionListener implements SessionMsgListener { private final CoapTransportResource coapTransportResource; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index ce1618fe99..72be5e6f1e 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.TbCoapServerComponent; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; import javax.annotation.PostConstruct; @@ -59,6 +60,8 @@ public class CoapTransportService implements TbTransportService { efento.add(efentoMeasurementsTransportResource); coapServer.add(api); coapServer.add(efento); + coapServer.add(new OtaPackageTransportResource(coapTransportContext, OtaPackageType.FIRMWARE)); + coapServer.add(new OtaPackageTransportResource(coapTransportContext, OtaPackageType.SOFTWARE)); log.info("CoAP transport started!"); } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java new file mode 100644 index 0000000000..60e8f55352 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java @@ -0,0 +1,144 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.security.DeviceTokenCredentials; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Slf4j +public class OtaPackageTransportResource extends AbstractCoapTransportResource { + private static final int ACCESS_TOKEN_POSITION = 2; + + private final OtaPackageType otaPackageType; + + public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { + super(ctx, otaPackageType.getKeyPrefix()); + this.otaPackageType = otaPackageType; + } + + @Override + protected void processHandleGet(CoapExchange exchange) { + log.trace("Processing {}", exchange.advanced().getRequest()); + exchange.accept(); + Exchange advanced = exchange.advanced(); + Request request = advanced.getRequest(); + processAccessTokenRequest(exchange, request); + } + + @Override + protected void processHandlePost(CoapExchange exchange) { + exchange.respond(CoAP.ResponseCode.METHOD_NOT_ALLOWED); + } + + private void processAccessTokenRequest(CoapExchange exchange, Request request) { + Optional credentials = decodeCredentials(request); + if (credentials.isEmpty()) { + exchange.respond(CoAP.ResponseCode.UNAUTHORIZED); + return; + } + transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), + new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> { + getOtaPackageCallback(sessionInfo, exchange, otaPackageType); + })); + } + + private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setType(firmwareType.name()).build(); + transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); + } + + private Optional decodeCredentials(Request request) { + List uriPath = request.getOptions().getUriPath(); + if (uriPath.size() == ACCESS_TOKEN_POSITION) { + return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); + } else { + return Optional.empty(); + } + } + + @Override + public Resource getChild(String name) { + return this; + } + + private class OtaPackageCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + OtaPackageCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) { + String title = exchange.getQueryParameter("title"); + String version = exchange.getQueryParameter("version"); + if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); + if ((title == null || msg.getTitle().equals(title)) && (version == null || msg.getVersion().equals(version))) { + String strChunkSize = exchange.getQueryParameter("size"); + String strChunk = exchange.getQueryParameter("chunk"); + int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); + int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); + respondOtaPackage(exchange, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); + } else { + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + } else { + exchange.respond(CoAP.ResponseCode.NOT_FOUND); + } + } + + @Override + public void onError(Throwable e) { + log.warn("Failed to process request", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + private void respondOtaPackage(CoapExchange exchange, byte[] data) { + Response response = new Response(CoAP.ResponseCode.CONTENT); + if (data != null && data.length > 0) { + response.setPayload(data); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + boolean moreFlag = data.length > chunkSize; + response.getOptions().setBlock2(chunkSize, moreFlag, 0); + } + exchange.respond(response); + } + } + +} From 209c93064a2b6251ebf95accf08b4684d884b83b Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 17 Jun 2021 19:36:15 +0300 Subject: [PATCH 101/151] Fix issue on LWM2M Security Store --- .../coap/OtaPackageTransportResource.java | 4 +-- .../server/LwM2mTransportCoapResource.java | 6 ++--- .../server/LwM2mTransportServerHelper.java | 26 ++----------------- .../server/store/TbLwM2mSecurityStore.java | 3 ++- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java index 60e8f55352..cb403c5e82 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java @@ -134,8 +134,8 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { response.setPayload(data); if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean moreFlag = data.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, 0); + boolean lastFlag = data.length > chunkSize; + response.getOptions().setBlock2(chunkSize, lastFlag, 0); } exchange.respond(response); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 229ec1a08c..b996922ded 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -143,9 +143,9 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { response.setPayload(fwData); if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean moreFlag = fwData.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, 0); - log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); + boolean lastFlag = fwData.length > chunkSize; + response.getOptions().setBlock2(chunkSize, lastFlag, 0); + log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag); } else { log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java index 9e36d35076..a50e979b77 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java @@ -76,32 +76,11 @@ public class LwM2mTransportServerHelper { return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000); } - /** - * send to Thingsboard Attribute || Telemetry - * - * @param msg - JsonObject: [{name: value}] - * @return - dummyWriteReplace {\"targetIdVer\":\"/19_1.0/0/0\",\"value\":0082} - */ - private TransportServiceCallback getPubAckCallbackSendAttrTelemetry(final T msg) { - return new TransportServiceCallback<>() { - @Override - public void onSuccess(Void dummy) { - log.trace("Success to publish msg: {}, dummy: {}", msg, dummy); - } - - @Override - public void onError(Throwable e) { - log.trace("[{}] Failed to publish msg: {}", msg, e); - } - }; - } - public void sendParametersOnThingsboardAttribute(List result, SessionInfoProto sessionInfo) { PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder(); request.addAllKv(result); PostAttributeMsg postAttributeMsg = request.build(); - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postAttributeMsg); - context.getTransportService().process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSendAttrTelemetry(call)); + context.getTransportService().process(sessionInfo, postAttributeMsg, TransportServiceCallback.EMPTY); } public void sendParametersOnThingsboardTelemetry(List result, SessionInfoProto sessionInfo) { @@ -111,8 +90,7 @@ public class LwM2mTransportServerHelper { builder.addAllKv(result); request.addTsKvList(builder.build()); PostTelemetryMsg postTelemetryMsg = request.build(); - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postTelemetryMsg); - context.getTransportService().process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSendAttrTelemetry(call)); + context.getTransportService().process(sessionInfo, postTelemetryMsg, TransportServiceCallback.EMPTY); } /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java index cbdb9faf07..293e68ec39 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java @@ -85,6 +85,7 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { @Override public void remove(String endpoint) { - securityStore.remove(endpoint); + //TODO: Make sure we delay removal of security store from endpoint due to reg/unreg race condition. +// securityStore.remove(endpoint); } } From be3e97d3e161933b93fdc6affc300e7dae7d0a15 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 18 Jun 2021 12:24:31 +0300 Subject: [PATCH 102/151] Improvements for CoAP --- .../lwm2m/NoSecLwM2MIntegrationTest.java | 2 +- .../lwm2m/X509LwM2MIntegrationTest.java | 7 +++- .../coapserver/DefaultCoapServerService.java | 13 ++++++- .../coap/OtaPackageTransportResource.java | 37 +++++++++++++++++++ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 1 + 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java index e94f432f64..d98a775c8c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java @@ -53,7 +53,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { private final int PORT = 5685; private final Security SECURITY = noSec("coap://localhost:" + PORT, 123); private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); - private final String ENDPOINT = "deviceAEndpoint"; + private final String ENDPOINT = "noSecEndpoint"; private Device createDevice() throws Exception { Device device = new Device(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java index a5efff968a..661f7c5474 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java @@ -19,6 +19,7 @@ import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.client.object.Security; import org.jetbrains.annotations.NotNull; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; @@ -74,11 +75,13 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { return device; } + //TODO: use different endpoints to isolate tests. + @Ignore() @Test public void testConnectAndObserveTelemetry() throws Exception { createDeviceProfile(TRANSPORT_CONFIGURATION); X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(endpoint); + credentials.setEndpoint(endpoint+1); Device device = createDevice(credentials); SingleEntityFilter sef = new SingleEntityFilter(); @@ -96,7 +99,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { wsClient.waitForReply(); wsClient.registerWaitForUpdate(); - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint+1); Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); client.init(security, coapConfig); String msg = wsClient.waitForUpdate(); diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 93f4e76551..605952e456 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -36,6 +36,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; + @Slf4j @Component @TbCoapServerComponent @@ -91,7 +93,16 @@ public class DefaultCoapServerService implements CoapServerService { InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); - noSecCoapEndpointBuilder.setNetworkConfig(NetworkConfig.getStandard()); + NetworkConfig networkConfig = new NetworkConfig(); + networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); + networkConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); + networkConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); + networkConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); + networkConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); + networkConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); + networkConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); + noSecCoapEndpointBuilder.setNetworkConfig(networkConfig); CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); server.addEndpoint(noSecCoapEndpoint); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java index cb403c5e82..bc9fdd9282 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java @@ -20,8 +20,10 @@ import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; +import org.eclipse.californium.core.server.resources.ResourceObserver; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -41,6 +43,8 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { super(ctx, otaPackageType.getKeyPrefix()); + this.setObservable(true); + this.addObserver(new OtaPackageTransportResource.CoapResourceObserver()); this.otaPackageType = otaPackageType; } @@ -135,10 +139,43 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); boolean lastFlag = data.length > chunkSize; + response.getOptions().setUriPath(exchange.getRequestOptions().getUriPathString()); response.getOptions().setBlock2(chunkSize, lastFlag, 0); } exchange.respond(response); } } + public class CoapResourceObserver implements ResourceObserver { + @Override + public void changedName(String old) { + + } + + @Override + public void changedPath(String old) { + + } + + @Override + public void addedChild(Resource child) { + + } + + @Override + public void removedChild(Resource child) { + + } + + @Override + public void addedObserveRelation(ObserveRelation relation) { + + } + + @Override + public void removedObserveRelation(ObserveRelation relation) { + + } + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 5b61ffc768..cd9c311b54 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -256,6 +256,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); try { log.warn("[{}] [{{}] Client: update after Registration", registration.getEndpoint(), registration.getId()); + logService.log(lwM2MClient, String.format("[%s][%s] Updated registration.", registration.getId(), registration.getSocketAddress())); clientContext.updateRegistration(lwM2MClient, registration); TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession(); this.reportActivityAndRegister(sessionInfo); From fe3378eab125b6f59ae316890f3ed4384b16f114 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 18 Jun 2021 12:58:38 +0300 Subject: [PATCH 103/151] UI: Add SNMP device transport configuration --- .../SnmpDeviceTransportConfiguration.java | 9 +- ...-profile-communication-config.component.ts | 4 +- ...snmp-device-profile-mapping.component.scss | 3 + ...ofile-transport-configuration.component.ts | 2 +- .../device/data/device-data.component.ts | 36 +++++- ...evice-transport-configuration.component.ts | 44 +++++-- ...ice-transport-configuration.component.html | 121 +++++++++++++++++- ...evice-transport-configuration.component.ts | 112 +++++++++++++--- ui-ngx/src/app/shared/models/device.models.ts | 60 ++++++++- .../assets/locale/locale.constant-en_US.json | 25 +++- 10 files changed, 366 insertions(+), 50 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java index a7bc143d81..d377ede985 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java @@ -51,6 +51,13 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur private String privacyPassphrase; private String engineId; + public SnmpDeviceTransportConfiguration() { + this.host = "localhost"; + this.port = 161; + this.protocolVersion = SnmpProtocolVersion.V2C; + this.community = "public"; + } + @Override public DeviceTransportType getType() { return DeviceTransportType.SNMP; @@ -76,7 +83,7 @@ public class SnmpDeviceTransportConfiguration implements DeviceTransportConfigur isValid = StringUtils.isNotBlank(username) && StringUtils.isNotBlank(securityName) && contextName != null && authenticationProtocol != null && StringUtils.isNotBlank(authenticationPassphrase) - && privacyProtocol != null && privacyPassphrase != null && engineId != null; + && privacyProtocol != null && StringUtils.isNotBlank(privacyPassphrase) && engineId != null; break; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts index d683d46880..7af2140efe 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts @@ -179,7 +179,7 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On if (isUndefinedOrNull(value)) { value = { spec: this.getFirstUnusedSeverity(), - queryingFrequencyMs: 0, + queryingFrequencyMs: 5000, mappings: null }; } @@ -196,7 +196,7 @@ export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, On ).subscribe(spec => { if (this.isShowFrequency(spec)) { form.addControl('queryingFrequencyMs', - this.fb.control(0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')])); + this.fb.control(5000, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')])); } else { form.removeControl('queryingFrequencyMs'); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss index 94cf2290ac..5972aee722 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss @@ -40,6 +40,9 @@ .mat-form-field-underline { bottom: 0; } + .mat-form-field-subscript-wrapper{ + margin-top: 1.8em; + } } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts index a384acbae3..400199ef9c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts @@ -86,7 +86,7 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements OnInit, ngOnInit(): void { this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ - timeoutMs: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + timeoutMs: [500, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], retries: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], communicationConfigs: [null, Validators.required], }); diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts index 1a510692db..a175764d89 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-data.component.ts @@ -15,7 +15,16 @@ /// import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @@ -29,13 +38,20 @@ import { selector: 'tb-device-data', templateUrl: './device-data.component.html', styleUrls: [], - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DeviceDataComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceDataComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DeviceDataComponent), + multi: true + }, + ] }) -export class DeviceDataComponent implements ControlValueAccessor, OnInit { +export class DeviceDataComponent implements ControlValueAccessor, OnInit, Validator { deviceDataFormGroup: FormGroup; @@ -97,6 +113,12 @@ export class DeviceDataComponent implements ControlValueAccessor, OnInit { this.deviceDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false}); } + validate(): ValidationErrors | null { + return this.deviceDataFormGroup.valid ? null : { + deviceDataForm: false + }; + } + private updateModel() { let deviceData: DeviceData = null; if (this.deviceDataFormGroup.valid) { diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts index 0e9d7e24a9..82359232a9 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts @@ -15,27 +15,39 @@ /// import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { - DeviceTransportConfiguration, - DeviceTransportType -} from '@shared/models/device.models'; +import { DeviceTransportConfiguration, DeviceTransportType } from '@shared/models/device.models'; import { deepClone } from '@core/utils'; @Component({ selector: 'tb-device-transport-configuration', templateUrl: './device-transport-configuration.component.html', styleUrls: [], - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DeviceTransportConfigurationComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceTransportConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DeviceTransportConfigurationComponent), + multi: true + }] }) -export class DeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit { +export class DeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit, Validator { deviceTransportType = DeviceTransportType; @@ -92,7 +104,15 @@ export class DeviceTransportConfigurationComponent implements ControlValueAccess if (configuration) { delete configuration.type; } - this.deviceTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); + setTimeout(() => { + this.deviceTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); + }, 0); + } + + validate(): ValidationErrors | null { + return this.deviceTransportConfigurationFormGroup.valid ? null : { + deviceTransportConfiguration: false + }; } private updateModel() { diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html index fc9f615db9..dadc98425e 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html @@ -15,10 +15,119 @@ limitations under the License. --> -

- - + +
+ + device-profile.snmp.host + + + {{ 'device-profile.snmp.host-required' | translate }} + + + + device-profile.snmp.port + + + {{ 'device-profile.snmp.port-required' | translate }} + + + {{ 'device-profile.snmp.port-format' | translate }} + + +
+ + device-profile.snmp.protocol-version + + + {{ snmpDeviceProtocolVersion | lowercase }} + + + + {{ 'device-profile.snmp.protocol-version-required' | translate }} + + +
+ + device-profile.snmp.community + + + {{ 'device-profile.snmp.community-required' | translate }} + + +
+
+
+ + device-profile.snmp.user-name + + + {{ 'device-profile.snmp.user-name-required' | translate }} + + + + device-profile.snmp.security-name + + + {{ 'device-profile.snmp.security-name-required' | translate }} + + +
+
+ + device-profile.snmp.authentication-protocol + + + {{ snmpAuthenticationProtocolTranslation.get(snmpAuthenticationProtocol) }} + + + + {{ 'device-profile.snmp.authentication-protocol-required' | translate }} + + + + device-profile.snmp.authentication-passphrase + + + {{ 'device-profile.snmp.authentication-passphrase-required' | translate }} + + +
+
+ + device-profile.snmp.privacy-protocol + + + {{ snmpPrivacyProtocolTranslation.get(snmpPrivacyProtocol) }} + + + + {{ 'device-profile.snmp.privacy-protocol-required' | translate }} + + + + device-profile.snmp.privacy-passphrase + + + {{ 'device-profile.snmp.privacy-passphrase-required' | translate }} + + +
+
+ + device-profile.snmp.context-name + + + + device-profile.snmp.engine-id + + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts index 165a2906f4..f35d538bfa 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts @@ -14,31 +14,57 @@ /// limitations under the License. /// -import {Component, forwardRef, Input, OnInit} from '@angular/core'; -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; -import {Store} from '@ngrx/store'; -import {AppState} from '@app/core/core.state'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceTransportConfiguration, DeviceTransportType, - SnmpDeviceTransportConfiguration + SnmpAuthenticationProtocol, + SnmpAuthenticationProtocolTranslationMap, + SnmpDeviceProtocolVersion, + SnmpDeviceTransportConfiguration, + SnmpPrivacyProtocol, + SnmpPrivacyProtocolTranslationMap } from '@shared/models/device.models'; +import { isDefinedAndNotNull } from '@core/utils'; @Component({ selector: 'tb-snmp-device-transport-configuration', templateUrl: './snmp-device-transport-configuration.component.html', styleUrls: [], - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SnmpDeviceTransportConfigurationComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SnmpDeviceTransportConfigurationComponent), + multi: true + }, { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SnmpDeviceTransportConfigurationComponent), + multi: true + }] }) -export class SnmpDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit { +export class SnmpDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit, Validator { snmpDeviceTransportConfigurationFormGroup: FormGroup; + snmpDeviceProtocolVersions = Object.values(SnmpDeviceProtocolVersion); + snmpAuthenticationProtocols = Object.values(SnmpAuthenticationProtocol); + snmpAuthenticationProtocolTranslation = SnmpAuthenticationProtocolTranslationMap; + snmpPrivacyProtocols = Object.values(SnmpPrivacyProtocol); + snmpPrivacyProtocolTranslation = SnmpPrivacyProtocolTranslationMap; + private requiredValue: boolean; get required(): boolean { @@ -53,8 +79,7 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc @Input() disabled: boolean; - private propagateChange = (v: any) => { - }; + private propagateChange = (v: any) => { }; constructor(private store: Store, private fb: FormBuilder) { @@ -69,13 +94,33 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc ngOnInit() { this.snmpDeviceTransportConfigurationFormGroup = this.fb.group({ - configuration: [null, Validators.required] + host: ['', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + port: [null, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + protocolVersion: [SnmpDeviceProtocolVersion.V2C, Validators.required], + community: ['public', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + username: ['', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + securityName: ['public', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + contextName: [null], + authenticationProtocol: [SnmpAuthenticationProtocol.SHA_512, Validators.required], + authenticationPassphrase: ['', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + privacyProtocol: [SnmpPrivacyProtocol.DES, Validators.required], + privacyPassphrase: ['', [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], + engineId: [''] + }); + this.snmpDeviceTransportConfigurationFormGroup.get('protocolVersion').valueChanges.subscribe((protocol: SnmpDeviceProtocolVersion) => { + this.updateDisabledFormValue(protocol); }); this.snmpDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); } + validate(): ValidationErrors | null { + return this.snmpDeviceTransportConfigurationFormGroup.valid ? null : { + snmpDeviceTransportConfiguration: false + }; + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -86,13 +131,46 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc } writeValue(value: SnmpDeviceTransportConfiguration | null): void { - this.snmpDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false}); + if (isDefinedAndNotNull(value)) { + this.snmpDeviceTransportConfigurationFormGroup.patchValue(value, {emitEvent: false}); + if (this.snmpDeviceTransportConfigurationFormGroup.enabled) { + this.updateDisabledFormValue(value.protocolVersion || SnmpDeviceProtocolVersion.V2C); + } + } + } + + isV3protocolVersion(): boolean { + return this.snmpDeviceTransportConfigurationFormGroup.get('protocolVersion').value === SnmpDeviceProtocolVersion.V3; + } + + private updateDisabledFormValue(protocol: SnmpDeviceProtocolVersion) { + if (protocol === SnmpDeviceProtocolVersion.V3) { + this.snmpDeviceTransportConfigurationFormGroup.get('community').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('username').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('securityName').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('contextName').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('authenticationProtocol').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('authenticationPassphrase').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('privacyProtocol').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('privacyPassphrase').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('engineId').enable({emitEvent: false}); + } else { + this.snmpDeviceTransportConfigurationFormGroup.get('community').enable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('username').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('securityName').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('contextName').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('authenticationProtocol').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('authenticationPassphrase').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('privacyProtocol').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('privacyPassphrase').disable({emitEvent: false}); + this.snmpDeviceTransportConfigurationFormGroup.get('engineId').disable({emitEvent: false}); + } } private updateModel() { let configuration: DeviceTransportConfiguration = null; if (this.snmpDeviceTransportConfigurationFormGroup.valid) { - configuration = this.snmpDeviceTransportConfigurationFormGroup.getRawValue().configuration; + configuration = this.snmpDeviceTransportConfigurationFormGroup.value; configuration.type = DeviceTransportType.SNMP; } this.propagateChange(configuration); diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 568faeb452..a766c3a6c3 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -362,7 +362,7 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT break; case DeviceTransportType.SNMP: const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = { - timeoutMs: 0, + timeoutMs: 500, retries: 0, communicationConfigs: null }; @@ -394,7 +394,12 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M}; break; case DeviceTransportType.SNMP: - const snmpTransportConfiguration: SnmpDeviceTransportConfiguration = {}; + const snmpTransportConfiguration: SnmpDeviceTransportConfiguration = { + host: 'localhost', + port: 161, + protocolVersion: SnmpDeviceProtocolVersion.V2C, + community: 'public' + }; transportConfiguration = {...snmpTransportConfiguration, type: DeviceTransportType.SNMP}; break; } @@ -572,8 +577,57 @@ export interface Lwm2mDeviceTransportConfiguration { [key: string]: any; } +export enum SnmpDeviceProtocolVersion { + V1 = 'V1', + V2C = 'V2C', + V3 = 'V3' +} + +export enum SnmpAuthenticationProtocol { + SHA_1 = 'SHA_1', + SHA_224 = 'SHA_224', + SHA_256 = 'SHA_256', + SHA_384 = 'SHA_384', + SHA_512 = 'SHA_512', + MD5 = 'MD%' +} + +export const SnmpAuthenticationProtocolTranslationMap = new Map([ + [SnmpAuthenticationProtocol.SHA_1, 'SHA-1'], + [SnmpAuthenticationProtocol.SHA_224, 'SHA-224'], + [SnmpAuthenticationProtocol.SHA_256, 'SHA-256'], + [SnmpAuthenticationProtocol.SHA_384, 'SHA-384'], + [SnmpAuthenticationProtocol.SHA_512, 'SHA-512'], + [SnmpAuthenticationProtocol.MD5, 'MD5'] +]); + +export enum SnmpPrivacyProtocol { + DES = 'DES', + AES_128 = 'AES_128', + AES_192 = 'AES_192', + AES_256 = 'AES_256' +} + +export const SnmpPrivacyProtocolTranslationMap = new Map([ + [SnmpPrivacyProtocol.DES, 'DES'], + [SnmpPrivacyProtocol.AES_128, 'AES-128'], + [SnmpPrivacyProtocol.AES_192, 'AES-192'], + [SnmpPrivacyProtocol.AES_256, 'AES-256'], +]); + export interface SnmpDeviceTransportConfiguration { - [key: string]: any; + host?: string; + port?: number; + protocolVersion?: SnmpDeviceProtocolVersion; + community?: string; + username?: string; + securityName?: string; + contextName?: string; + authenticationProtocol?: SnmpAuthenticationProtocol; + authenticationPassphrase?: string; + privacyProtocol?: SnmpPrivacyProtocol; + privacyPassphrase?: string; + engineId?: string; } export type DeviceTransportConfigurations = DefaultDeviceTransportConfiguration & diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 95dc0c2d72..ff07be44da 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1301,16 +1301,35 @@ "snmp": { "add-communication-config": "Add communication config", "add-mapping": "Add mapping", + "authentication-passphrase": "Authentication passphrase", + "authentication-passphrase-required": "Authentication passphrase is required.", + "authentication-protocol": "Authentication protocol", + "authentication-protocol-required": "Authentication protocol is required.", "communication-configs": "Communication configs", + "community": "Community string", + "community-required": "Community string is required.", + "context-name": "Context name", "data-key": "Data key", "data-key-required": "Data key is required.", "data-type": "Data type", "data-type-required": "Data type is required.", + "engine-id": "Engine ID", + "host": "Host", + "host-required": "Host is required.", "oid": "OID", "oid-pattern": "Invalid OID format.", "oid-required": "OID is required.", "please-add-communication-config": "Please add communication config", "please-add-mapping-config": "Please add mapping config", + "port": "Port", + "port-format": "Invalid port format.", + "port-required": "Port is required.", + "privacy-passphrase": "Privacy passphrase", + "privacy-passphrase-required": "Privacy passphrase is required.", + "privacy-protocol": "Privacy protocol", + "privacy-protocol-required": "Privacy protocol is required.", + "protocol-version": "Protocol version", + "protocol-version-required": "Protocol version is required.", "querying-frequency": "Querying frequency, ms", "querying-frequency-invalid-format": "Querying frequency must be a positive integer.", "querying-frequency-required": "Querying frequency is required.", @@ -1319,9 +1338,13 @@ "retries-required": "Retries is required.", "scope": "Scope", "scope-required": "Scope is required.", + "security-name": "Security name", + "security-name-required": "Security name is required.", "timeout-ms": "Timeout, ms", "timeout-ms-invalid-format": "Timeout must be a positive integer.", - "timeout-ms-required": "Timeout is required." + "timeout-ms-required": "Timeout is required.", + "user-name": "User name", + "user-name-required": "User name is required." } }, "dialog": { From e3c523cc6454db8a6b10aa63b1c542444028a45e Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Fri, 18 Jun 2021 14:33:31 +0300 Subject: [PATCH 104/151] Write replace command fix --- .../lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java | 2 +- .../server/transport/lwm2m/server/rpc/IdOrKeyRequest.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 5de223fa2f..0432a5b742 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -206,7 +206,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { } private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { - RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); + RpcWriteReplaceRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteReplaceRequest.class); TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) .value(requestBody.getValue()) .timeout(this.config.getTimeout()).build(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java index 1055b01232..77d8cd3ea1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/IdOrKeyRequest.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.transport.lwm2m.server.rpc; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @Data +@JsonIgnoreProperties(ignoreUnknown = true) public class IdOrKeyRequest { private String key; From e470a032a9f2fa12d949f6ed83df3ba7ef39e58b Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 18 Jun 2021 17:21:35 +0300 Subject: [PATCH 105/151] improved countByDeviceProfileAndEmptyOtaPackage api --- .../server/controller/DeviceController.java | 10 ++++++---- .../java/org/thingsboard/rest/client/RestClient.java | 2 +- ui-ngx/src/app/core/http/ota-package.service.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index b13393515c..5c7df50d09 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -782,15 +782,17 @@ public class DeviceController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET) + @RequestMapping(value = "/devices/count/{otaPackageType}/{deviceProfileId}", method = RequestMethod.GET) @ResponseBody - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType, - @RequestParam String deviceProfileId) throws ThingsboardException { + public Long countByDeviceProfileAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType, + @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException { checkParameter("OtaPackageType", otaPackageType); checkParameter("DeviceProfileId", deviceProfileId); try { return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType)); + getTenantId(), + new DeviceProfileId(UUID.fromString(deviceProfileId)), + OtaPackageType.valueOf(otaPackageType)); } catch (Exception e) { throw handleException(e); } diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index b716935b2a..6fbf28f24e 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -1257,7 +1257,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { params.put("deviceProfileId", deviceProfileId.getId().toString()); return restTemplate.exchange( - baseURL + "/api/devices/count/{otaPackageType}?deviceProfileId={deviceProfileId}", + baseURL + "/api/devices/count/{otaPackageType}/{deviceProfileId}", HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference() { diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 1f9a36882a..a806751825 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -133,7 +133,7 @@ export class OtaPackageService { } public countUpdateDeviceAfterChangePackage(type: OtaUpdateType, entityId: EntityId, config?: RequestConfig): Observable { - return this.http.get(`/api/devices/count/${type}?deviceProfileId=${entityId.id}`, defaultHttpOptionsFromConfig(config)); + return this.http.get(`/api/devices/count/${type}/${entityId.id}`, defaultHttpOptionsFromConfig(config)); } public confirmDialogUpdatePackage(entity: BaseData&OtaPagesIds, From 5b998717e79f7fcde249b9a0a3686a2c0f5be68e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 18 Jun 2021 18:00:12 +0300 Subject: [PATCH 106/151] Remove invalid test --- .../CachedAttributesServiceTest.java | 88 ------------------- 1 file changed, 88 deletions(-) delete mode 100644 dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java diff --git a/dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java deleted file mode 100644 index af980a6301..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/attributes/CachedAttributesServiceTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL - * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. - * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. - */ -package org.thingsboard.server.dao.attributes; - -import com.google.common.util.concurrent.MoreExecutors; -import org.junit.Test; -import org.thingsboard.server.dao.cache.CacheConfiguration; -import org.thingsboard.server.dao.cache.CacheExecutorService; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willCallRealMethod; -import static org.mockito.Mockito.mock; - -public class CachedAttributesServiceTest { - - public static final String REDIS = "redis"; - - @Test - public void givenLocalCacheTypeName_whenEquals_thenOK() { - assertThat(CachedAttributesService.LOCAL_CACHE_TYPE, is("caffeine")); - } - - @Test - public void givenCacheType_whenGetExecutor_thenDirectExecutor() { - CachedAttributesService cachedAttributesService = mock(CachedAttributesService.class); - CacheExecutorService cacheExecutorService = mock(CacheExecutorService.class); - willCallRealMethod().given(cachedAttributesService).getExecutor(any(), any()); - - assertThat(cachedAttributesService.getExecutor(null, cacheExecutorService), is(MoreExecutors.directExecutor())); - - CacheConfiguration cacheConfiguration = new CacheConfiguration(); - cacheConfiguration.setType(null); - assertThat(cachedAttributesService.getExecutor(cacheConfiguration, cacheExecutorService), is(MoreExecutors.directExecutor())); - - cacheConfiguration.setType(""); - assertThat(cachedAttributesService.getExecutor(cacheConfiguration, cacheExecutorService), is(MoreExecutors.directExecutor())); - - cacheConfiguration.setType(CachedAttributesService.LOCAL_CACHE_TYPE); - assertThat(cachedAttributesService.getExecutor(cacheConfiguration, cacheExecutorService), is(MoreExecutors.directExecutor())); - - } - - @Test - public void givenCacheType_whenGetExecutor_thenReturnCacheExecutorService() { - CachedAttributesService cachedAttributesService = mock(CachedAttributesService.class); - CacheExecutorService cacheExecutorService = mock(CacheExecutorService.class); - willCallRealMethod().given(cachedAttributesService).getExecutor(any(CacheConfiguration.class), any(CacheExecutorService.class)); - - CacheConfiguration cacheConfiguration = new CacheConfiguration(); - cacheConfiguration.setType(REDIS); - assertThat(cachedAttributesService.getExecutor(cacheConfiguration, cacheExecutorService), is(cacheExecutorService)); - - cacheConfiguration.setType("unknownCacheType"); - assertThat(cachedAttributesService.getExecutor(cacheConfiguration, cacheExecutorService), is(cacheExecutorService)); - - } - -} \ No newline at end of file From d64a0c382d7bac67224db60879ce640e8d3bd59a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 18 Jun 2021 18:22:48 +0300 Subject: [PATCH 107/151] Fix test properties --- dao/src/test/resources/application-test.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index d7a960471b..a257c8fbce 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -10,6 +10,7 @@ audit-log.default_query_period=30 audit-log.sink.type=none cache.type=caffeine +cache.maximumPoolSize=16 #cache.type=redis caffeine.specs.relations.timeToLiveInMinutes=1440 From a7a7c801f2ea59f72cec1c92007fb9c0ad6d4804 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 18 Jun 2021 18:31:52 +0300 Subject: [PATCH 108/151] Fix license header --- .../dao/cache/CacheExecutorService.java | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/CacheExecutorService.java b/dao/src/main/java/org/thingsboard/server/dao/cache/CacheExecutorService.java index ca00e41d79..9f0f54bb46 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cache/CacheExecutorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/CacheExecutorService.java @@ -1,32 +1,17 @@ /** - * ThingsBoard, Inc. ("COMPANY") CONFIDENTIAL + * Copyright © 2016-2021 The Thingsboard Authors * - * Copyright © 2016-2021 ThingsBoard, Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * NOTICE: All information contained herein is, and remains - * the property of ThingsBoard, Inc. and its suppliers, - * if any. The intellectual and technical concepts contained - * herein are proprietary to ThingsBoard, Inc. - * and its suppliers and may be covered by U.S. and Foreign Patents, - * patents in process, and are protected by trade secret or copyright law. + * http://www.apache.org/licenses/LICENSE-2.0 * - * Dissemination of this information or reproduction of this material is strictly forbidden - * unless prior written permission is obtained from COMPANY. - * - * Access to the source code contained herein is hereby forbidden to anyone except current COMPANY employees, - * managers or contractors who have executed Confidentiality and Non-disclosure agreements - * explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes - * information that is confidential and/or proprietary, and is a trade secret, of COMPANY. - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, - * OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT - * THE EXPRESS WRITTEN CONSENT OF COMPANY IS STRICTLY PROHIBITED, - * AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. - * THE RECEIPT OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION - * DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, - * OR TO MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.thingsboard.server.dao.cache; From c03893fb570ae659e41493f4801d62463207d956 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 19 Jun 2021 16:58:32 +0300 Subject: [PATCH 109/151] LWM2M: fix bug update FwUrl in client after change in profile --- .../coapserver/DefaultCoapServerService.java | 18 +++++++++++- .../transport/coap/CoapTransportService.java | 13 ++++++++- .../coap/OtaPackageTransportResource.java | 15 ++++++++-- ...LwM2mCredentialsSecurityInfoValidator.java | 3 ++ .../server/LwM2mTransportCoapResource.java | 4 +-- .../ota/DefaultLwM2MOtaUpdateService.java | 29 +++++++++++++++++-- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 8 +++-- .../server/ota/LwM2MOtaUpdateService.java | 4 +++ .../uplink/DefaultLwM2MUplinkMsgHandler.java | 27 +++++++++++++++-- ...ile-transport-configuration.component.html | 4 +-- ...ofile-transport-configuration.component.ts | 6 ++-- .../lwm2m/lwm2m-profile-config.models.ts | 4 +-- 12 files changed, 112 insertions(+), 23 deletions(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 93f4e76551..211ccecc3e 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -36,6 +36,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; + @Slf4j @Component @TbCoapServerComponent @@ -91,7 +93,21 @@ public class DefaultCoapServerService implements CoapServerService { InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); - noSecCoapEndpointBuilder.setNetworkConfig(NetworkConfig.getStandard()); + + NetworkConfig coapConfig = new NetworkConfig(); + coapConfig.setInt(NetworkConfig.Keys.COAP_PORT, 5683); + coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT, 5684); + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); + coapConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); + coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); + + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); + + noSecCoapEndpointBuilder.setNetworkConfig(coapConfig); CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); server.addEndpoint(noSecCoapEndpoint); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index 72be5e6f1e..a5adc36d7e 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -18,11 +18,12 @@ package org.thingsboard.server.transport.coap; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapServer; +import org.eclipse.californium.core.network.config.NetworkConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.coapserver.CoapServerService; import org.thingsboard.server.coapserver.TbCoapServerComponent; +import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; @@ -30,6 +31,8 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.net.UnknownHostException; +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; + @Service("CoapTransportService") @TbCoapServerComponent @Slf4j @@ -52,6 +55,14 @@ public class CoapTransportService implements TbTransportService { public void init() throws UnknownHostException { log.info("Starting CoAP transport..."); coapServer = coapServerService.getCoapServer(); + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); + coapServer.getConfig().setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); + coapServer.getConfig().setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); + coapServer.getConfig().setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); CoapResource api = new CoapResource(API); api.add(new CoapTransportResource(coapTransportContext, coapServerService, V1)); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java index cb403c5e82..989239549c 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java @@ -17,11 +17,13 @@ package org.thingsboard.server.transport.coap; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; +import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -32,16 +34,21 @@ import org.thingsboard.server.gen.transport.TransportProtos; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ExecutorService; @Slf4j public class OtaPackageTransportResource extends AbstractCoapTransportResource { private static final int ACCESS_TOKEN_POSITION = 2; private final OtaPackageType otaPackageType; + private final ExecutorService sendOtaDataOutUriLarge; public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { super(ctx, otaPackageType.getKeyPrefix()); this.otaPackageType = otaPackageType; + + this.setObservable(true); + this.sendOtaDataOutUriLarge = ThingsBoardExecutors.newWorkStealingPool(10, "LwM2M sendOtaDataOutUriLarge"); } @Override @@ -132,11 +139,13 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { Response response = new Response(CoAP.ResponseCode.CONTENT); if (data != null && data.length > 0) { response.setPayload(data); + response.getOptions().setAccept(MediaTypeRegistry.APPLICATION_OCTET_STREAM); if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean lastFlag = data.length > chunkSize; - response.getOptions().setBlock2(chunkSize, lastFlag, 0); - } + boolean lastFlag = data.length <= chunkSize; + this.sendOtaDataOutUriLarge.submit(() -> { + response.getOptions().setBlock2(chunkSize, lastFlag, 0); + }); } exchange.respond(response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 13d6fd6568..1ec48b31f5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -58,10 +58,12 @@ public class LwM2mCredentialsSecurityInfoValidator { public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) { CountDownLatch latch = new CountDownLatch(1); final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1]; + log.warn("001) [{}]", credentialsId); context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(), new TransportServiceCallback<>() { @Override public void onSuccess(ValidateDeviceCredentialsResponse msg) { + log.warn("002) [{}] [{}]", credentialsId, msg); String credentialsBody = msg.getCredentials(); resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue); resultSecurityStore[0].setMsg(msg); @@ -71,6 +73,7 @@ public class LwM2mCredentialsSecurityInfoValidator { @Override public void onError(Throwable e) { + log.warn("003) [{}] [{}] Failed to process credentials ", credentialsId, e); log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e); resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null); latch.countDown(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index b996922ded..f6456a394c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -25,8 +25,6 @@ import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.ResourceObserver; import org.thingsboard.server.cache.ota.OtaPackageDataCache; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -143,7 +141,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { response.setPayload(fwData); if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - boolean lastFlag = fwData.length > chunkSize; + boolean lastFlag = fwData.length <= chunkSize; response.getOptions().setBlock2(chunkSize, lastFlag, 0); log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index d44770e591..4126541176 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; @@ -174,6 +175,24 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl fwInfo.setCurrentName(name); } + @Override + public void onCurrentFirmwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl) { + log.debug("[{}] Current fw strategy: {}", client.getEndpoint(), newStrategy); + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setFwStrategy(LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(newStrategy)); + fwInfo.setBaseUrl(newBaseUrl); + startFirmwareUpdateIfNeeded(client, fwInfo); + } + + @Override + public void onCurrentSoftwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl) { + log.debug("[{}] Current sw strategy: {}", client.getEndpoint(), newStrategy); + LwM2MClientOtaInfo swInfo = getOrInitSwInfo(client); + swInfo.setSwStrategy(LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(newStrategy)); + swInfo.setBaseUrl(newBaseUrl); + startSoftwareUpdateIfNeeded(client, swInfo); + } + @Override public void onCurrentFirmwareVersion3Update(LwM2mClient client, String version) { log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); @@ -250,6 +269,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } } + private void startSoftwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo swInfo) { + + } + private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); @@ -277,7 +300,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MFirmwareUpdateStrategy strategy; if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { - strategy = fwInfo.getStrategy(); + strategy = fwInfo.getFwStrategy(); } else { strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } @@ -328,9 +351,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl return Optional.empty(); } - private LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { + public LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { //TODO: fetch state from the cache or DB. - return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { + return this.fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { var profile = clientContext.getProfile(client.getProfileId()); return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(), profile.getClientLwM2mSettings().getFwUpdateRecourse()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 43d4f4acf5..74bf8d1fac 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -19,8 +19,9 @@ import lombok.Data; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; import java.util.Optional; @@ -44,7 +45,8 @@ public class LwM2MClientOtaInfo { private Integer deliveryMethod; //TODO: use value from device if applicable; - private LwM2MFirmwareUpdateStrategy strategy; + private LwM2MFirmwareUpdateStrategy fwStrategy; + private LwM2MSoftwareUpdateStrategy swStrategy; private UpdateStateFw updateState; private UpdateResultFw updateResult; @@ -54,7 +56,7 @@ public class LwM2MClientOtaInfo { public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { this.endpoint = endpoint; this.type = type; - this.strategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); + this.fwStrategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); this.baseUrl = baseUrl; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index 9c7905a5e8..644099ee9d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -31,6 +31,10 @@ public interface LwM2MOtaUpdateService { void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); + void onCurrentFirmwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl); + + void onCurrentSoftwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl); + void onCurrentFirmwareVersion3Update(LwM2mClient client, String version); void onCurrentFirmwareVersion5Update(LwM2mClient client, String version); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 5b61ffc768..0810d1a423 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -37,7 +37,6 @@ import org.eclipse.leshan.server.registration.Registration; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.DonAsynchron; -import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -53,6 +52,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; @@ -100,7 +101,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -779,6 +779,29 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl clients.forEach(client -> sendCancelObserveRequest(targetId, client)); } } + + // # 7.1 + // update value in fwInfo + if (!newProfile.getClientLwM2mSettings().getFwUpdateStrategy().equals(oldProfile.getClientLwM2mSettings().getFwUpdateStrategy()) + || (LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == newProfile.getClientLwM2mSettings().getFwUpdateStrategy() && + !newProfile.getClientLwM2mSettings().getFwUpdateRecourse().equals(oldProfile.getClientLwM2mSettings().getFwUpdateRecourse()))) { + clients.forEach(lwM2MClient -> { + otaService.onCurrentFirmwareStrategyUpdate(lwM2MClient, + newProfile.getClientLwM2mSettings().getFwUpdateStrategy(), + newProfile.getClientLwM2mSettings().getFwUpdateRecourse()); + }); + } + + //# 7.2 // update value in swInfo + if (!newProfile.getClientLwM2mSettings().getSwUpdateStrategy().equals(oldProfile.getClientLwM2mSettings().getSwUpdateStrategy()) + || (LwM2MSoftwareUpdateStrategy.TEMP_URL.code == newProfile.getClientLwM2mSettings().getSwUpdateStrategy() && + !newProfile.getClientLwM2mSettings().getSwUpdateRecourse().equals(oldProfile.getClientLwM2mSettings().getSwUpdateRecourse()))) { + clients.forEach(lwM2MClient -> { + otaService.onCurrentSoftwareStrategyUpdate(lwM2MClient, + newProfile.getClientLwM2mSettings().getFwUpdateStrategy(), + newProfile.getClientLwM2mSettings().getFwUpdateRecourse()); + }); + } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 30b78393d3..5286161651 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -160,9 +160,9 @@ - + - + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index faf94eccce..f99f1629ab 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -97,7 +97,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: [], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], - clientStrategy: [1, []], + clientOnlyObserveAfterConnect: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], fwUpdateRecourse: [{value: '', disabled: true}, []], @@ -216,7 +216,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: this.configurationValue.bootstrap.servers.binding, bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, - clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, fwUpdateRecourse: fwResource, @@ -257,7 +257,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; bootstrapServers.notifIfDisabled = config.notifIfDisabled; bootstrapServers.binding = config.binding; - this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; + this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect; this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 1f2d125d9f..594147e5af 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -168,7 +168,7 @@ export interface Lwm2mProfileConfigModels { } export interface ClientLwM2mSettings { - clientStrategy: string; + clientOnlyObserveAfterConnect: number; fwUpdateStrategy: number; swUpdateStrategy: number; fwUpdateRecourse: string; @@ -240,7 +240,7 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientStrategy: '1', + clientOnlyObserveAfterConnect: 1, fwUpdateStrategy: 1, swUpdateStrategy: 1, fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, From a34ffe8a409e353cf73faeaf83e6cc22c518a0ed Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 20 Jun 2021 14:09:20 +0300 Subject: [PATCH 110/151] LWM2M: merge with master --- .../secure/LwM2MBootstrapSecurityStore.java | 8 +- ...LwM2mCredentialsSecurityInfoValidator.java | 9 +- .../TbLwM2MDtlsCertificateVerifier.java | 5 +- .../server/DefaultLwM2mTransportService.java | 2 +- .../server/LwM2mTransportCoapResource.java | 4 +- .../lwm2m/server/LwM2mTransportUtil.java | 352 ++---------------- .../lwm2m/server/client/LwM2mClient.java | 6 +- .../ota/DefaultLwM2MOtaUpdateService.java | 123 +++++- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 8 +- .../LwM2MFirmwareUpdateStrategy.java | 2 +- .../server/ota/firmware/UpdateDeliveryFw.java | 55 +++ .../{ => ota/firmware}/UpdateResultFw.java | 2 +- .../{ => ota/firmware}/UpdateStateFw.java | 2 +- .../LwM2MSoftwareUpdateStrategy.java | 2 +- .../server/ota/software/UpdateResultSw.java | 84 +++++ .../server/ota/software/UpdateStateSw.java | 62 +++ .../server/store/TbLwM2mSecurityStore.java | 11 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 16 +- .../lwm2m/server/uplink/LwM2mTypeServer.java | 39 ++ 19 files changed, 408 insertions(+), 384 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => ota/firmware}/LwM2MFirmwareUpdateStrategy.java (96%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => ota/firmware}/UpdateResultFw.java (97%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => ota/firmware}/UpdateStateFw.java (96%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{ => ota/software}/LwM2MSoftwareUpdateStrategy.java (95%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mTypeServer.java diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java index 80aa42d6af..7740778adf 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapSecurityStore.java @@ -29,12 +29,11 @@ import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import java.io.IOException; import java.security.GeneralSecurityException; @@ -46,6 +45,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP; @Slf4j @Service("LwM2MBootstrapSecurityStore") @@ -68,7 +68,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public Iterator getAllByEndpoint(String endPoint) { - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP); if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { /* add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); @@ -92,7 +92,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @Override public SecurityInfo getByIdentity(String identity) { - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP); if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { /* add value to store from BootstrapJson */ this.setBootstrapConfigScurityInfo(store); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 1ec48b31f5..8bd97a952e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -33,7 +33,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer; import java.io.IOException; import java.security.GeneralSecurityException; @@ -45,6 +45,7 @@ import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.eclipse.leshan.core.SecurityMode.PSK; import static org.eclipse.leshan.core.SecurityMode.RPK; import static org.eclipse.leshan.core.SecurityMode.X509; +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP; @Slf4j @Component @@ -55,7 +56,7 @@ public class LwM2mCredentialsSecurityInfoValidator { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; - public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) { + public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTypeServer keyValue) { CountDownLatch latch = new CountDownLatch(1); final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1]; log.warn("001) [{}]", credentialsId); @@ -94,11 +95,11 @@ public class LwM2mCredentialsSecurityInfoValidator { * @param keyValue - * @return SecurityInfo */ - private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) { + private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) { TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class); if (credentials != null) { - if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) { + if (keyValue.equals(BOOTSTRAP)) { result.setBootstrapCredentialConfig(credentials.getBootstrap()); if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) { PSKClientCredentials pskClientConfig = (PSKClientCredentials) credentials.getClient(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index dcd7fa89bb..792ba131e8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -42,7 +42,6 @@ import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; @@ -57,6 +56,8 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; + @Slf4j @Component @TbLwM2mTransportComponent @@ -117,7 +118,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer String strCert = SslUtil.getCertificateString(cert); String sha3Hash = EncryptionUtil.getSha3Hash(strCert); - TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT); + TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT); ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null; if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) { LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 9425ef7891..c027267197 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -65,7 +65,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE; @Slf4j @Component diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index f6456a394c..105943eab8 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -31,8 +31,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RECOURSE; @Slf4j public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index d04e4993f1..64f80f7353 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -43,13 +43,13 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.ota.OtaPackageKey; -import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; -import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateResultSw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateStateSw; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import java.util.ArrayList; @@ -77,277 +77,20 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_UPDATE_STATE_ID; @Slf4j public class LwM2mTransportUtil { - public static final String EVENT_AWAKE = "AWAKE"; - public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ"; - public static final String RESPONSE_CHANNEL = "RESP"; - public static final String OBSERVE_CHANNEL = "OBSERVE"; - - public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; - public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; - public static final String BOOTSTRAP = "bootstrap"; - public static final String SERVERS = "servers"; - public static final String LWM2M_SERVER = "lwm2mServer"; - public static final String BOOTSTRAP_SERVER = "bootstrapServer"; - public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr"; - public static final String ATTRIBUTE = "attribute"; - public static final String TELEMETRY = "telemetry"; - public static final String KEY_NAME = "keyName"; - public static final String OBSERVE_LWM2M = "observe"; - public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; - - private static final String REQUEST = "/request"; - private static final String ATTRIBUTES = "/" + ATTRIBUTE; - public static final String TELEMETRIES = "/" + TELEMETRY; - public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; - public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; - - public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms + public static final String LWM2M_VERSION_DEFAULT = "1.0"; public static final String LOG_LWM2M_TELEMETRY = "logLwm2m"; public static final String LOG_LWM2M_INFO = "info"; public static final String LOG_LWM2M_ERROR = "error"; public static final String LOG_LWM2M_WARN = "warn"; - public static final String LOG_LWM2M_VALUE = "value"; - - public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; - public static final String LWM2M_VERSION_DEFAULT = "1.0"; - - // Firmware - public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw"; - public static final String FW_UPDATE = "Firmware update"; - public static final Integer FW_5_ID = 5; - public static final Integer FW_19_ID = 19; - - // Package W - public static final String FW_PACKAGE_5_ID = "/5/0/0"; - public static final String FW_PACKAGE_19_ID = "/19/0/0"; - // Package URI - public static final String FW_PACKAGE_URI_ID = "/5/0/1"; - // State R - public static final String FW_STATE_ID = "/5/0/3"; - // Update Result R - public static final String FW_RESULT_ID = "/5/0/5"; - - public static final String FW_DELIVERY_METHOD = "/5/0/9"; - - // PkgName R - public static final String FW_NAME_ID = "/5/0/6"; - // PkgVersion R - public static final String FW_5_VER_ID = "/5/0/7"; - - /** - * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 - * BC68JAR01A10 - * # Request prodct type number - * ATI - * Quectel - * BC68 - * Revision:BC68JAR01A10 - */ - public static final String FW_3_VER_ID = "/3/0/3"; - // Update E - public static final String FW_UPDATE_ID = "/5/0/2"; - - // Software - public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; - public static final String SW_UPDATE = "Software update"; - public static final Integer SW_ID = 9; - // Package W - public static final String SW_PACKAGE_ID = "/9/0/2"; - // Package URI - public static final String SW_PACKAGE_URI_ID = "/9/0/3"; - // Update State R - public static final String SW_UPDATE_STATE_ID = "/9/0/7"; - // Update Result R - public static final String SW_RESULT_ID = "/9/0/9"; - // PkgName R - public static final String SW_NAME_ID = "/9/0/0"; - // PkgVersion R - public static final String SW_VER_ID = "/9/0/1"; - // Install E - public static final String SW_INSTALL_ID = "/9/0/4"; - // Uninstall E - public static final String SW_UN_INSTALL_ID = "/9/0/6"; - - public enum LwM2mTypeServer { - BOOTSTRAP(0, "bootstrap"), - CLIENT(1, "client"); - - public int code; - public String type; - - LwM2mTypeServer(int code, String type) { - this.code = code; - this.type = type; - } - - public static LwM2mTypeServer fromLwM2mTypeServer(String type) { - for (LwM2mTypeServer sm : LwM2mTypeServer.values()) { - if (sm.type.equals(type)) { - return sm; - } - } - throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type)); - } - } - - public static Optional toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { - switch (updateResultFw) { - case INITIAL: - return Optional.empty(); - case UPDATE_SUCCESSFULLY: - return Optional.of(UPDATED); - case NOT_ENOUGH: - case OUT_OFF_MEMORY: - case CONNECTION_LOST: - case INTEGRITY_CHECK_FAILURE: - case UNSUPPORTED_TYPE: - case INVALID_URI: - case UPDATE_FAILED: - case UNSUPPORTED_PROTOCOL: - return Optional.of(FAILED); - default: - throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); - } - } - - public static Optional toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { - switch (updateStateFw) { - case IDLE: - return Optional.empty(); - case DOWNLOADING: - return Optional.of(DOWNLOADING); - case DOWNLOADED: - return Optional.of(DOWNLOADED); - case UPDATING: - return Optional.of(UPDATING); - default: - throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw); - } - } - - /** - * SW Update State R - * 0: INITIAL Before downloading. (see 5.1.2.1) - * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2) - * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3) - * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4) - * If executing the Install Resource failed, the state remains at DELIVERED. - * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED. - * After executing the UnInstall Resource, the state changes to INITIAL. - * 4: INSTALLED - */ - public enum UpdateStateSw { - INITIAL(0, "Initial"), - DOWNLOAD_STARTED(1, "DownloadStarted"), - DOWNLOADED(2, "Downloaded"), - DELIVERED(3, "Delivered"), - INSTALLED(4, "Installed"); - - public int code; - public String type; - - UpdateStateSw(int code, String type) { - this.code = code; - this.type = type; - } - - public static UpdateStateSw fromUpdateStateSwByType(String type) { - for (UpdateStateSw to : UpdateStateSw.values()) { - if (to.type.equals(type)) { - return to; - } - } - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type)); - } - - public static UpdateStateSw fromUpdateStateSwByCode(int code) { - for (UpdateStateSw to : UpdateStateSw.values()) { - if (to.code == code) { - return to; - } - } - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code)); - } - } - - /** - * SW Update Result - * Contains the result of downloading or installing/uninstalling the software - * 0: Initial value. - * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value. - * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0". - * 1: Downloading. - * - The package downloading process is on-going. - * 2: Software successfully installed. - * 3: Successfully Downloaded and package integrity verified - * (( 4-49, for expansion, of other scenarios)) - * ** Failed - * 50: Not enough storage for the new software package. - * 51: Out of memory during downloading process. - * 52: Connection lost during downloading process. - * 53: Package integrity check failure. - * 54: Unsupported package type. - * 56: Invalid URI - * 57: Device defined update error - * 58: Software installation failure - * 59: Uninstallation Failure during forUpdate(arg=0) - * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features) - * This Resource MAY be reported by sending Observe operation. - */ - public enum UpdateResultSw { - INITIAL(0, "Initial value", false), - DOWNLOADING(1, "Downloading", false), - SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false), - SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false), - NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true), - OUT_OFF_MEMORY(51, "Out of memory during downloading process", true), - CONNECTION_LOST(52, "Connection lost during downloading process", false), - PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false), - UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false), - INVALID_URI(56, "Invalid URI", true), - UPDATE_ERROR(57, "Device defined update error", true), - INSTALL_FAILURE(58, "Software installation failure", true), - UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true); - - public int code; - public String type; - public boolean isAgain; - - UpdateResultSw(int code, String type, boolean isAgain) { - this.code = code; - this.type = type; - this.isAgain = isAgain; - } - - public static UpdateResultSw fromUpdateResultSwByType(String type) { - for (UpdateResultSw to : UpdateResultSw.values()) { - if (to.type.equals(type)) { - return to; - } - } - throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type)); - } - - public static UpdateResultSw fromUpdateResultSwByCode(int code) { - for (UpdateResultSw to : UpdateResultSw.values()) { - if (to.code == code) { - return to; - } - } - throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code)); - } - } public enum LwM2MClientStrategy { CLIENT_STRATEGY_1(1, "Read only resources marked as observation"), @@ -380,45 +123,7 @@ public class LwM2mTransportUtil { } } - /** - * FirmwareUpdateStatus { - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED - */ - public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { - switch (updateResultSw) { - case INITIAL: - switch (updateStateSw) { - case INITIAL: - case DOWNLOAD_STARTED: - return DOWNLOADING; - case DOWNLOADED: - return DOWNLOADED; - case DELIVERED: - return VERIFIED; - } - case DOWNLOADING: - return DOWNLOADING; - case SUCCESSFULLY_INSTALLED: - return UPDATED; - case SUCCESSFULLY_DOWNLOADED_VERIFIED: - return VERIFIED; - case NOT_ENOUGH_STORAGE: - case OUT_OFF_MEMORY: - case CONNECTION_LOST: - case PACKAGE_CHECK_FAILURE: - case UNSUPPORTED_PACKAGE_TYPE: - case INVALID_URI: - case UPDATE_ERROR: - case INSTALL_FAILURE: - case UN_INSTALL_FAILURE: - return FAILED; - default: - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name()); - } - } - - - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { switch (type) { case BOOLEAN: @@ -477,18 +182,6 @@ public class LwM2mTransportUtil { return null; } -// public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { -// LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); -// lwM2MClientProfile.setTenantId(tenantId); -// lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject()); -// lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject()); -// lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); -// lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); -// lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray()); -// lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); -// return lwM2MClientProfile; -// } - public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) { DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) { @@ -603,7 +296,6 @@ public class LwM2mTransportUtil { if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) { return pathIdVer; } else { - LwM2mPath pathObjId = new LwM2mPath(pathIdVer); return convertObjectIdToVersionedId(pathIdVer, registration); } } @@ -743,18 +435,18 @@ public class LwM2mTransportUtil { } } - public static boolean isFwSwWords(String pathName) { - return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); - } +// public static boolean isFwSwWords(String pathName) { +// return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) +// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); +// } /** * @param lwM2MClient - diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 0e71a1e865..dbcb82a167 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -33,12 +33,10 @@ import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Collection; import java.util.Map; @@ -53,7 +51,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_VERSION_DEFAULT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; @@ -307,7 +305,7 @@ public class LwM2mClient implements Cloneable { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verRez = getVerFromPathIdVerOrId(path); - return verRez == null ? TRANSPORT_DEFAULT_LWM2M_VERSION.equals(verSupportedObject) : verRez.equals(verSupportedObject); + return verRez == null ? LWM2M_VERSION_DEFAULT.equals(verSupportedObject) : verRez.equals(verSupportedObject); } /** diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 4126541176..8f0a9da33f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.request.ContentFormat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -32,12 +33,7 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; @@ -48,6 +44,13 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateDeliveryFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateResultSw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateStateSw; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import javax.annotation.PostConstruct; @@ -60,8 +63,13 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @@ -78,13 +86,30 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); + public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw"; + public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "tbsw"; private static final String FW_PACKAGE_5_ID = "/5/0/0"; private static final String FW_URL_ID = "/5/0/1"; private static final String FW_EXECUTE_ID = "/5/0/2"; - private static final String FW_NAME_ID = "/5/0/6"; - private static final String FW_VER_ID = "/5/0/7"; + public static final String FW_STATE_ID = "/5/0/3"; + public static final String FW_RESULT_ID = "/5/0/5"; + public static final String FW_NAME_ID = "/5/0/6"; + public static final String FW_5_VER_ID = "/5/0/7"; + /** + * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 + * Revision:BC68JAR01A10 + */ + public static final String FW_3_VER_ID = "/3/0/3"; + public static final String FW_DELIVERY_METHOD = "/5/0/9"; + private static final String SW_NAME_ID = "/9/0/0"; private static final String SW_VER_ID = "/9/0/1"; + public static final String SW_PACKAGE_ID = "/9/0/2"; + public static final String SW_PACKAGE_URI_ID = "/9/0/3"; + public static final String SW_INSTALL_ID = "/9/0/4"; + public static final String SW_UPDATE_STATE_ID = "/9/0/7"; + public static final String SW_RESULT_ID = "/9/0/9"; + public static final String SW_UN_INSTALL_ID = "/9/0/6"; private final Map fwStates = new ConcurrentHashMap<>(); private final Map swStates = new ConcurrentHashMap<>(); @@ -216,7 +241,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl executeFwUpdate(client); } fwInfo.setUpdateState(state); - Optional status = LwM2mTransportUtil.toOtaPackageUpdateStatus(state); + Optional status = this.toOtaPackageUpdateStatus(state); status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, otaStatus, "Firmware Update State: " + state.name())); } @@ -226,7 +251,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl log.debug("[{}] Current fw result: {}", client.getEndpoint(), code); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); - Optional status = LwM2mTransportUtil.toOtaPackageUpdateStatus(result); + Optional status = this.toOtaPackageUpdateStatus(result); status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, otaStatus, "Firmware Update Result: " + result.name())); if (result.isAgain() && fwInfo.getRetryAttempts() <= 2) { @@ -299,10 +324,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MFirmwareUpdateStrategy strategy; - if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == UpdateDeliveryFw.BOTH.code) { strategy = fwInfo.getFwStrategy(); } else { - strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; + strategy = fwInfo.getDeliveryMethod() == UpdateDeliveryFw.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } switch (strategy) { case OBJ_5_BINARY: @@ -380,4 +405,78 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); } + private static Optional toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { + switch (updateResultFw) { + case INITIAL: + return Optional.empty(); + case UPDATE_SUCCESSFULLY: + return Optional.of(UPDATED); + case NOT_ENOUGH: + case OUT_OFF_MEMORY: + case CONNECTION_LOST: + case INTEGRITY_CHECK_FAILURE: + case UNSUPPORTED_TYPE: + case INVALID_URI: + case UPDATE_FAILED: + case UNSUPPORTED_PROTOCOL: + return Optional.of(FAILED); + default: + throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); + } + } + + private static Optional toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { + switch (updateStateFw) { + case IDLE: + return Optional.empty(); + case DOWNLOADING: + return Optional.of(DOWNLOADING); + case DOWNLOADED: + return Optional.of(DOWNLOADED); + case UPDATING: + return Optional.of(UPDATING); + default: + throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw); + } + } + + /** + * FirmwareUpdateStatus { + * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED + */ + public static Optional toSwSateResultUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { + switch (updateResultSw) { + case INITIAL: + switch (updateStateSw) { + case INITIAL: + case DOWNLOAD_STARTED: + return Optional.of(DOWNLOADING); + case DOWNLOADED: + return Optional.of(DOWNLOADED); + case DELIVERED: + return Optional.of(VERIFIED); + } + case DOWNLOADING: + return Optional.of(DOWNLOADING); + case SUCCESSFULLY_INSTALLED: + return Optional.of(UPDATED); + case SUCCESSFULLY_DOWNLOADED_VERIFIED: + return Optional.of(VERIFIED); + case NOT_ENOUGH_STORAGE: + case OUT_OFF_MEMORY: + case CONNECTION_LOST: + case PACKAGE_CHECK_FAILURE: + case UNSUPPORTED_PACKAGE_TYPE: + case INVALID_URI: + case UPDATE_ERROR: + case INSTALL_FAILURE: + case UN_INSTALL_FAILURE: + return Optional.of(FAILED); + default: + throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name()); + } + } + + + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 74bf8d1fac..98e2055fdc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -18,10 +18,10 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import lombok.Data; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; import java.util.Optional; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MFirmwareUpdateStrategy.java similarity index 96% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MFirmwareUpdateStrategy.java index f111152b76..d8ce5b1fbb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MFirmwareUpdateStrategy.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.ota.firmware; public enum LwM2MFirmwareUpdateStrategy { OBJ_5_BINARY(1, "ObjectId 5, Binary"), diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java new file mode 100644 index 0000000000..dcb93a5283 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota.firmware; + +/** + * /** Delivery Method R + * 0: Pull only + * 1: Push only + * 2: Both: + * - In this case the LwM2M Server MAY choose the preferred mechanism for conveying the firmware image to the LwM2M Client. + */ +public enum UpdateDeliveryFw { + PULL(0, "Pull only"), + PUSH(1, "Push only"), + BOTH(2, "Push or Push"); + + public int code; + public String type; + + UpdateDeliveryFw(int code, String type) { + this.code = code; + this.type = type; + } + + public static UpdateDeliveryFw fromStateFwByType(String type) { + for (UpdateDeliveryFw to : UpdateDeliveryFw.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW delivery type : %s", type)); + } + + public static UpdateDeliveryFw fromStateFwByCode(int code) { + for (UpdateDeliveryFw to : UpdateDeliveryFw.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW delivery code : %s", code)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java similarity index 97% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java index 79361aeb1d..5a71f41bc5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.ota.firmware; import lombok.Getter; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java similarity index 96% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java index b47a96e9bd..447749fd50 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.ota.firmware; /** * /** State R diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/LwM2MSoftwareUpdateStrategy.java similarity index 95% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/LwM2MSoftwareUpdateStrategy.java index 0df3f5ca2d..5fbdc25a2f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/LwM2MSoftwareUpdateStrategy.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.transport.lwm2m.server; +package org.thingsboard.server.transport.lwm2m.server.ota.software; public enum LwM2MSoftwareUpdateStrategy { BINARY(1, "ObjectId 9, Binary"), diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java new file mode 100644 index 0000000000..22c92205d0 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota.software; + +/** + * SW Update Result + * Contains the result of downloading or installing/uninstalling the software + * 0: Initial value. + * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value. + * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0". + * 1: Downloading. + * - The package downloading process is on-going. + * 2: Software successfully installed. + * 3: Successfully Downloaded and package integrity verified + * (( 4-49, for expansion, of other scenarios)) + * ** Failed + * 50: Not enough storage for the new software package. + * 51: Out of memory during downloading process. + * 52: Connection lost during downloading process. + * 53: Package integrity check failure. + * 54: Unsupported package type. + * 56: Invalid URI + * 57: Device defined update error + * 58: Software installation failure + * 59: Uninstallation Failure during forUpdate(arg=0) + * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features) + * This Resource MAY be reported by sending Observe operation. + */ +public enum UpdateResultSw { + INITIAL(0, "Initial value", false), + DOWNLOADING(1, "Downloading", false), + SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false), + SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false), + NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true), + OUT_OFF_MEMORY(51, "Out of memory during downloading process", true), + CONNECTION_LOST(52, "Connection lost during downloading process", false), + PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false), + UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false), + INVALID_URI(56, "Invalid URI", true), + UPDATE_ERROR(57, "Device defined update error", true), + INSTALL_FAILURE(58, "Software installation failure", true), + UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true); + + public int code; + public String type; + public boolean isAgain; + + UpdateResultSw(int code, String type, boolean isAgain) { + this.code = code; + this.type = type; + this.isAgain = isAgain; + } + + public static UpdateResultSw fromUpdateResultSwByType(String type) { + for (UpdateResultSw to : UpdateResultSw.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type)); + } + + public static UpdateResultSw fromUpdateResultSwByCode(int code) { + for (UpdateResultSw to : UpdateResultSw.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java new file mode 100644 index 0000000000..a5f772a11d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.ota.software; + +/** + * SW Update State R + * 0: INITIAL Before downloading. (see 5.1.2.1) + * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2) + * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3) + * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4) + * If executing the Install Resource failed, the state remains at DELIVERED. + * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED. + * After executing the UnInstall Resource, the state changes to INITIAL. + * 4: INSTALLED + */ +public enum UpdateStateSw { + INITIAL(0, "Initial"), + DOWNLOAD_STARTED(1, "DownloadStarted"), + DOWNLOADED(2, "Downloaded"), + DELIVERED(3, "Delivered"), + INSTALLED(4, "Installed"); + + public int code; + public String type; + + UpdateStateSw(int code, String type) { + this.code = code; + this.type = type; + } + + public static UpdateStateSw fromUpdateStateSwByType(String type) { + for (UpdateStateSw to : UpdateStateSw.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type)); + } + + public static UpdateStateSw fromUpdateStateSwByCode(int code) { + for (UpdateStateSw to : UpdateStateSw.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code)); + } +} + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java index 293e68ec39..d47be49978 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java @@ -16,20 +16,13 @@ package org.thingsboard.server.transport.lwm2m.server.store; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; import org.eclipse.leshan.server.security.SecurityInfo; -import org.eclipse.leshan.server.security.SecurityStore; -import org.eclipse.leshan.server.security.SecurityStoreListener; import org.jetbrains.annotations.Nullable; -import org.springframework.stereotype.Component; -import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import java.util.Collection; +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; @Slf4j public class TbLwM2mSecurityStore implements TbEditableSecurityStore { @@ -67,7 +60,7 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { @Nullable public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) { - TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, LwM2mTransportUtil.LwM2mTypeServer.CLIENT); + TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT); try { if (securityInfo != null) { securityStore.put(securityInfo); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index cf158aa14e..3307db2fca 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -52,8 +52,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; @@ -83,6 +81,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; @@ -105,18 +105,18 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_DELIVERY_METHOD; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_5_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_DELIVERY_METHOD; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_NAME_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID; @Slf4j diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mTypeServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mTypeServer.java new file mode 100644 index 0000000000..0ecd59f41c --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mTypeServer.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.uplink; + +public enum LwM2mTypeServer { + BOOTSTRAP(0, "bootstrap"), + CLIENT(1, "client"); + + public int code; + public String type; + + LwM2mTypeServer(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2mTypeServer fromLwM2mTypeServer(String type) { + for (LwM2mTypeServer sm : LwM2mTypeServer.values()) { + if (sm.type.equals(type)) { + return sm; + } + } + throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type)); + } +} + From d3e16ad65009f7c80869dae628bf3aba47685071 Mon Sep 17 00:00:00 2001 From: ShvaykaD Date: Tue, 22 Jun 2021 13:31:08 +0300 Subject: [PATCH 111/151] updated observe sessions closing actions --- .../transport/coap/CoapTransportResource.java | 123 +++++++++++------- .../transport/http/DeviceApiController.java | 6 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 10 +- .../transport/mqtt/MqttTransportHandler.java | 5 +- .../mqtt/session/GatewayDeviceSessionCtx.java | 6 +- .../snmp/session/DeviceSessionContext.java | 6 +- .../common/transport/SessionMsgListener.java | 4 +- .../service/DefaultTransportService.java | 4 +- 8 files changed, 102 insertions(+), 62 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 95cdeada88..9f5d78b5aa 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -64,6 +64,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; @Slf4j public class CoapTransportResource extends AbstractCoapTransportResource { @@ -75,9 +76,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4; private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID"; - private final ConcurrentMap tokenToSessionInfoMap = new ConcurrentHashMap<>(); - private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); - private final ConcurrentMap sessionInfoToObserveRelationMap = new ConcurrentHashMap<>(); + private final ConcurrentMap tokenToCoapSessionInfoMap = new ConcurrentHashMap<>(); + private final ConcurrentMap sessionInfoToObserveRelationMap = new ConcurrentHashMap<>(); private final Set rpcSubscriptions = ConcurrentHashMap.newKeySet(); private final Set attributeSubscriptions = ConcurrentHashMap.newKeySet(); @@ -93,7 +93,11 @@ public class CoapTransportResource extends AbstractCoapTransportResource { this.timeout = coapServerService.getTimeout(); this.sessionReportTimeout = ctx.getSessionReportTimeout(); ctx.getScheduler().scheduleAtFixedRate(() -> { - Set observeSessions = sessionInfoToObserveRelationMap.keySet(); + Set coapObserveSessionInfos = sessionInfoToObserveRelationMap.keySet(); + Set observeSessions = coapObserveSessionInfos + .stream() + .map(CoapObserveSessionInfo::getSessionInfoProto) + .collect(Collectors.toSet()); observeSessions.forEach(this::reportActivity); }, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS); } @@ -111,17 +115,17 @@ public class CoapTransportResource extends AbstractCoapTransportResource { relation.setEstablished(); addObserveRelation(relation); } - AtomicInteger notificationCounter = tokenToObserveNotificationSeqMap.computeIfAbsent(token, s -> new AtomicInteger(0)); - response.getOptions().setObserve(notificationCounter.getAndIncrement()); + AtomicInteger observeNotificationCounter = tokenToCoapSessionInfoMap.get(token).getObserveNotificationCounter(); + response.getOptions().setObserve(observeNotificationCounter.getAndIncrement()); } // ObserveLayer takes care of the else case } - public void clearAndNotifyObserveRelation(ObserveRelation relation, CoAP.ResponseCode code) { + private void clearAndNotifyObserveRelation(ObserveRelation relation, CoAP.ResponseCode code) { relation.cancel(); relation.getExchange().sendResponse(new Response(code)); } - public Map getSessionInfoToObserveRelationMap() { + private Map getCoapSessionInfoToObserveRelationMap() { return sessionInfoToObserveRelationMap; } @@ -277,8 +281,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); break; case SUBSCRIBE_ATTRIBUTES_REQUEST: - TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionInfoMap.get(getTokenFromRequest(request)); - if (currentAttrSession == null) { + CoapObserveSessionInfo currentCoapObserveAttrSessionInfo = tokenToCoapSessionInfoMap.get(getTokenFromRequest(request)); + if (currentCoapObserveAttrSessionInfo == null) { attributeSubscriptions.add(sessionId); registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request)); @@ -290,20 +294,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } break; case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(getTokenFromRequest(request)); - if (attrSession != null) { + CoapObserveSessionInfo coapObserveAttrSessionInfo = lookupAsyncSessionInfo(getTokenFromRequest(request)); + if (coapObserveAttrSessionInfo != null) { + TransportProtos.SessionInfoProto attrSession = coapObserveAttrSessionInfo.getSessionInfoProto(); UUID attrSessionId = toSessionId(attrSession); attributeSubscriptions.remove(attrSessionId); - sessionInfoToObserveRelationMap.remove(attrSession); transportService.process(attrSession, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(), - new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); - closeAndDeregister(sessionInfo); + new CoapNoOpCallback(exchange)); } + closeAndDeregister(sessionInfo); break; case SUBSCRIBE_RPC_COMMANDS_REQUEST: - TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionInfoMap.get(getTokenFromRequest(request)); - if (currentRpcSession == null) { + CoapObserveSessionInfo currentCoapObserveRpcSessionInfo = tokenToCoapSessionInfoMap.get(getTokenFromRequest(request)); + if (currentCoapObserveRpcSessionInfo == null) { rpcSubscriptions.add(sessionId); registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request)); @@ -314,16 +318,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } break; case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(getTokenFromRequest(request)); - if (rpcSession != null) { + CoapObserveSessionInfo coapObserveRpcSessionInfo = lookupAsyncSessionInfo(getTokenFromRequest(request)); + if (coapObserveRpcSessionInfo != null) { + TransportProtos.SessionInfoProto rpcSession = coapObserveRpcSessionInfo.getSessionInfoProto(); UUID rpcSessionId = toSessionId(rpcSession); rpcSubscriptions.remove(rpcSessionId); - sessionInfoToObserveRelationMap.remove(rpcSession); transportService.process(rpcSession, TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(), new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); - closeAndDeregister(sessionInfo); } + closeAndDeregister(sessionInfo); break; case TO_DEVICE_RPC_RESPONSE: transportService.process(sessionInfo, @@ -355,13 +359,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource { return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); } - private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { - tokenToObserveNotificationSeqMap.remove(token); - return tokenToSessionInfoMap.remove(token); + private CoapObserveSessionInfo lookupAsyncSessionInfo(String token) { + return tokenToCoapSessionInfoMap.remove(token); } private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) { - tokenToSessionInfoMap.putIfAbsent(token, sessionInfo); + tokenToCoapSessionInfoMap.putIfAbsent(token, new CoapObserveSessionInfo(sessionInfo)); transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder)); transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); } @@ -474,40 +477,33 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } @Override - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) { + public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg msg) { + log.trace("[{}] Received attributes update notification to device", sessionId); try { exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + closeAndDeregister(); } } @Override public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage()); - Map sessionToObserveRelationMap = coapTransportResource.getSessionInfoToObserveRelationMap(); - if (coapTransportResource.getObserverCount() > 0 && !CollectionUtils.isEmpty(sessionToObserveRelationMap)) { - Set observeSessions = sessionToObserveRelationMap.keySet(); - Optional observeSessionToClose = observeSessions.stream().filter(sessionInfoProto -> { - UUID observeSessionId = new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); - return observeSessionId.equals(sessionId); - }).findFirst(); - if (observeSessionToClose.isPresent()) { - TransportProtos.SessionInfoProto sessionInfoProto = observeSessionToClose.get(); - ObserveRelation observeRelation = sessionToObserveRelationMap.get(sessionInfoProto); - coapTransportResource.clearAndNotifyObserveRelation(observeRelation, CoAP.ResponseCode.SERVICE_UNAVAILABLE); - } - } + closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.SERVICE_UNAVAILABLE); + closeAndDeregister(); } @Override - public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { + public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg msg) { + log.trace("[{}] Received RPC command to device", sessionId); try { exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + closeObserveRelationAndNotify(sessionId, CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + closeAndDeregister(); } } @@ -524,6 +520,30 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private boolean isConRequest() { return exchange.advanced().getRequest().isConfirmable(); } + + private void closeObserveRelationAndNotify(UUID sessionId, CoAP.ResponseCode responseCode) { + Map sessionToObserveRelationMap = coapTransportResource.getCoapSessionInfoToObserveRelationMap(); + if (coapTransportResource.getObserverCount() > 0 && !CollectionUtils.isEmpty(sessionToObserveRelationMap)) { + Optional observeSessionToClose = sessionToObserveRelationMap.keySet().stream().filter(coapObserveSessionInfo -> { + TransportProtos.SessionInfoProto sessionToDelete = coapObserveSessionInfo.getSessionInfoProto(); + UUID observeSessionId = new UUID(sessionToDelete.getSessionIdMSB(), sessionToDelete.getSessionIdLSB()); + return observeSessionId.equals(sessionId); + }).findFirst(); + if (observeSessionToClose.isPresent()) { + CoapObserveSessionInfo coapObserveSessionInfo = observeSessionToClose.get(); + ObserveRelation observeRelation = sessionToObserveRelationMap.get(coapObserveSessionInfo); + coapTransportResource.clearAndNotifyObserveRelation(observeRelation, responseCode); + } + } + } + + private void closeAndDeregister() { + Request request = exchange.advanced().getRequest(); + String token = coapTransportResource.getTokenFromRequest(request); + CoapObserveSessionInfo deleted = coapTransportResource.lookupAsyncSessionInfo(token); + coapTransportResource.closeAndDeregister(deleted.getSessionInfoProto()); + } + } public class CoapResourceObserver implements ResourceObserver { @@ -548,7 +568,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { public void addedObserveRelation(ObserveRelation relation) { Request request = relation.getExchange().getRequest(); String token = getTokenFromRequest(request); - sessionInfoToObserveRelationMap.putIfAbsent(tokenToSessionInfoMap.get(token), relation); + sessionInfoToObserveRelationMap.putIfAbsent(tokenToCoapSessionInfoMap.get(token), relation); log.trace("Added Observe relation for token: {}", token); } @@ -556,8 +576,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { public void removedObserveRelation(ObserveRelation relation) { Request request = relation.getExchange().getRequest(); String token = getTokenFromRequest(request); - TransportProtos.SessionInfoProto session = tokenToSessionInfoMap.get(token); - sessionInfoToObserveRelationMap.remove(session); + sessionInfoToObserveRelationMap.remove(tokenToCoapSessionInfoMap.get(token)); log.trace("Relation removed for token: {}", token); } } @@ -568,7 +587,6 @@ public class CoapTransportResource extends AbstractCoapTransportResource { transportService.deregisterSession(session); rpcSubscriptions.remove(sessionId); attributeSubscriptions.remove(sessionId); - sessionInfoToObserveRelationMap.remove(session); } private TransportConfigurationContainer getTransportConfigurationContainer(DeviceProfile deviceProfile) throws AdaptorException { @@ -634,4 +652,17 @@ public class CoapTransportResource extends AbstractCoapTransportResource { this.jsonPayload = jsonPayload; } } + + @Data + private static class CoapObserveSessionInfo { + + private final TransportProtos.SessionInfoProto sessionInfoProto; + private final AtomicInteger observeNotificationCounter; + + private CoapObserveSessionInfo(TransportProtos.SessionInfoProto sessionInfoProto) { + this.sessionInfoProto = sessionInfoProto; + this.observeNotificationCounter = new AtomicInteger(0); + } + } + } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index f4a3f705af..79ff2b70d1 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -386,7 +386,8 @@ public class DeviceApiController implements TbTransportService { } @Override - public void onAttributeUpdate(AttributeUpdateNotificationMsg msg) { + public void onAttributeUpdate(UUID sessionId, AttributeUpdateNotificationMsg msg) { + log.trace("[{}] Received attributes update notification to device", sessionId); responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK)); } @@ -397,7 +398,8 @@ public class DeviceApiController implements TbTransportService { } @Override - public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) { + public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg msg) { + log.trace("[{}] Received RPC command to device", sessionId); responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index f39ced6dfc..b2059d9557 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -52,9 +52,10 @@ public class LwM2mSessionMsgListener implements GenericFutureListener Date: Tue, 22 Jun 2021 14:27:56 +0300 Subject: [PATCH 112/151] Update getOAuth2Clients Rest client method --- .../java/org/thingsboard/rest/client/RestClient.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 6fbf28f24e..2d0914c7d4 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -104,6 +104,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -1772,7 +1773,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }).getBody(); } - public List getOAuth2Clients(String pkgName) { + public List getOAuth2Clients(String pkgName, PlatformType platformType) { Map params = new HashMap<>(); StringBuilder urlBuilder = new StringBuilder(baseURL); urlBuilder.append("/api/noauth/oauth2Clients"); @@ -1780,6 +1781,15 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { urlBuilder.append("?pkgName={pkgName}"); params.put("pkgName", pkgName); } + if (platformType != null) { + if (pkgName != null) { + urlBuilder.append("&"); + } else { + urlBuilder.append("?"); + } + urlBuilder.append("platform={platform}"); + params.put("platform", platformType.name()); + } return restTemplate.exchange( urlBuilder.toString(), HttpMethod.POST, From e83064ece73ec11726c131e56c47949f6d70e7e2 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 22 Jun 2021 15:53:06 +0300 Subject: [PATCH 113/151] Refactoring of the LwM2MTransport --- application/src/test/resources/logback.xml | 2 +- .../coap/OtaPackageTransportResource.java | 42 +------------ .../config/LwM2MTransportServerConfig.java | 2 +- ...LwM2mCredentialsSecurityInfoValidator.java | 5 +- .../lwm2m/server/LwM2mTransportUtil.java | 29 +++------ .../ota/DefaultLwM2MOtaUpdateService.java | 59 +++++++++---------- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 10 ++-- .../server/ota/LwM2MOtaUpdateService.java | 5 +- ...eryFw.java => FirmwareDeliveryMethod.java} | 19 ++---- ...esultFw.java => FirmwareUpdateResult.java} | 12 ++-- ...eStateFw.java => FirmwareUpdateState.java} | 12 ++-- ...esultSw.java => SoftwareUpdateResult.java} | 12 ++-- ...eStateSw.java => SoftwareUpdateState.java} | 12 ++-- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 44 +++++--------- 14 files changed, 98 insertions(+), 167 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/{UpdateDeliveryFw.java => FirmwareDeliveryMethod.java} (70%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/{UpdateResultFw.java => FirmwareUpdateResult.java} (86%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/{UpdateStateFw.java => FirmwareUpdateState.java} (80%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/{UpdateResultSw.java => SoftwareUpdateResult.java} (89%) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/{UpdateStateSw.java => SoftwareUpdateState.java} (84%) diff --git a/application/src/test/resources/logback.xml b/application/src/test/resources/logback.xml index 69e5d98193..d3301bf660 100644 --- a/application/src/test/resources/logback.xml +++ b/application/src/test/resources/logback.xml @@ -14,7 +14,7 @@ - + diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java index 224dd61e2a..2aadea26b2 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/OtaPackageTransportResource.java @@ -42,14 +42,12 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { private static final int ACCESS_TOKEN_POSITION = 2; private final OtaPackageType otaPackageType; - private final ExecutorService sendOtaDataOutUriLarge; public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { super(ctx, otaPackageType.getKeyPrefix()); this.otaPackageType = otaPackageType; this.setObservable(true); - this.sendOtaDataOutUriLarge = ThingsBoardExecutors.newWorkStealingPool(10, "LwM2M sendOtaDataOutUriLarge"); } @Override @@ -140,46 +138,12 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { Response response = new Response(CoAP.ResponseCode.CONTENT); if (data != null && data.length > 0) { response.setPayload(data); -// response.getOptions().setAccept(MediaTypeRegistry.APPLICATION_OCTET_STREAM); if (exchange.getRequestOptions().getBlock2() != null) { int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); boolean lastFlag = data.length <= chunkSize; - this.sendOtaDataOutUriLarge.submit(() -> { - response.getOptions().setBlock2(chunkSize, lastFlag, 0); - }); } - exchange.respond(response); - } - } - - public class CoapResourceObserver implements ResourceObserver { - @Override - public void changedName(String old) { - - } - - @Override - public void changedPath(String old) { - - } - - @Override - public void addedChild(Resource child) { - - } - - @Override - public void removedChild(Resource child) { - - } - - @Override - public void addedObserveRelation(ObserveRelation relation) { - - } - - @Override - public void removedObserveRelation(ObserveRelation relation) { - + response.getOptions().setBlock2(chunkSize, lastFlag, 0); + } + transportContext.getExecutor().submit(() -> exchange.respond(response)); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index a2ff361712..730effaf3c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -142,7 +142,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { URI uri = null; try { uri = Resources.getResource(keyStorePathFile).toURI(); - log.error("URI: {}", uri); + log.info("URI: {}", uri); File keyStoreFile = new File(uri); InputStream inKeyStore = new FileInputStream(keyStoreFile); keyStoreValue = KeyStore.getInstance(keyStoreType); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index 8bd97a952e..d11f39c7f2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -59,12 +59,12 @@ public class LwM2mCredentialsSecurityInfoValidator { public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTypeServer keyValue) { CountDownLatch latch = new CountDownLatch(1); final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1]; - log.warn("001) [{}]", credentialsId); + log.trace("Validating credentials [{}]", credentialsId); context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(), new TransportServiceCallback<>() { @Override public void onSuccess(ValidateDeviceCredentialsResponse msg) { - log.warn("002) [{}] [{}]", credentialsId, msg); + log.trace("Validated credentials: [{}] [{}]", credentialsId, msg); String credentialsBody = msg.getCredentials(); resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue); resultSecurityStore[0].setMsg(msg); @@ -74,7 +74,6 @@ public class LwM2mCredentialsSecurityInfoValidator { @Override public void onError(Throwable e) { - log.warn("003) [{}] [{}] Failed to process credentials ", credentialsId, e); log.trace("[{}] [{}] Failed to process credentials ", credentialsId, e); resultSecurityStore[0] = createSecurityInfo(credentialsId, null, null); latch.countDown(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 64f80f7353..bbab28ded5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -46,10 +46,10 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; -import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateResultSw; -import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateStateSw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState; +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult; +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import java.util.ArrayList; @@ -148,19 +148,19 @@ public class LwM2mTransportUtil { if (path != null) { if (FW_STATE_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); - lwM2mOtaConvert.setValue(UpdateStateFw.fromStateFwByCode(((Long) value).intValue()).type); + lwM2mOtaConvert.setValue(FirmwareUpdateState.fromStateFwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; } else if (FW_RESULT_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); - lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).getType()); + lwM2mOtaConvert.setValue(FirmwareUpdateResult.fromUpdateResultFwByCode(((Long) value).intValue()).getType()); return lwM2mOtaConvert; } else if (SW_UPDATE_STATE_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); - lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type); + lwM2mOtaConvert.setValue(SoftwareUpdateState.fromUpdateStateSwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; } else if (SW_RESULT_ID.equals(path)) { lwM2mOtaConvert.setCurrentType(STRING); - lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type); + lwM2mOtaConvert.setValue(SoftwareUpdateResult.fromUpdateResultSwByCode(((Long) value).intValue()).type); return lwM2mOtaConvert; } } @@ -435,19 +435,6 @@ public class LwM2mTransportUtil { } } -// public static boolean isFwSwWords(String pathName) { -// return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) -// || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); -// } - /** * @param lwM2MClient - * @param path - diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 8f0a9da33f..d3f1e8eb9d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; @@ -45,12 +46,12 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplac import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateDeliveryFw; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateResultSw; -import org.thingsboard.server.transport.lwm2m.server.ota.software.UpdateStateSw; +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult; +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import javax.annotation.PostConstruct; @@ -201,20 +202,20 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl } @Override - public void onCurrentFirmwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl) { - log.debug("[{}] Current fw strategy: {}", client.getEndpoint(), newStrategy); + public void onCurrentFirmwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration) { + log.debug("[{}] Current fw strategy: {}", client.getEndpoint(), configuration.getFwUpdateStrategy()); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - fwInfo.setFwStrategy(LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(newStrategy)); - fwInfo.setBaseUrl(newBaseUrl); + fwInfo.setFwStrategy(LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(configuration.getFwUpdateStrategy())); + fwInfo.setBaseUrl(configuration.getFwUpdateRecourse()); startFirmwareUpdateIfNeeded(client, fwInfo); } @Override - public void onCurrentSoftwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl) { - log.debug("[{}] Current sw strategy: {}", client.getEndpoint(), newStrategy); + public void onCurrentSoftwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration) { + log.debug("[{}] Current sw strategy: {}", client.getEndpoint(), configuration.getSwUpdateStrategy()); LwM2MClientOtaInfo swInfo = getOrInitSwInfo(client); - swInfo.setSwStrategy(LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(newStrategy)); - swInfo.setBaseUrl(newBaseUrl); + swInfo.setSwStrategy(LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(configuration.getSwUpdateStrategy())); + swInfo.setBaseUrl(configuration.getSwUpdateRecourse()); startSoftwareUpdateIfNeeded(client, swInfo); } @@ -236,8 +237,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long stateCode) { log.debug("[{}] Current fw state: {}", client.getEndpoint(), stateCode); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - UpdateStateFw state = UpdateStateFw.fromStateFwByCode(stateCode.intValue()); - if (UpdateStateFw.DOWNLOADED.equals(state)) { + FirmwareUpdateState state = FirmwareUpdateState.fromStateFwByCode(stateCode.intValue()); + if (FirmwareUpdateState.DOWNLOADED.equals(state)) { executeFwUpdate(client); } fwInfo.setUpdateState(state); @@ -250,7 +251,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { log.debug("[{}] Current fw result: {}", client.getEndpoint(), code); LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); - UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); + FirmwareUpdateResult result = FirmwareUpdateResult.fromUpdateResultFwByCode(code.intValue()); Optional status = this.toOtaPackageUpdateStatus(result); status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, otaStatus, "Firmware Update Result: " + result.name())); @@ -324,10 +325,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); LwM2MFirmwareUpdateStrategy strategy; - if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == UpdateDeliveryFw.BOTH.code) { + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == FirmwareDeliveryMethod.BOTH.code) { strategy = fwInfo.getFwStrategy(); } else { - strategy = fwInfo.getDeliveryMethod() == UpdateDeliveryFw.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; + strategy = fwInfo.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; } switch (strategy) { case OBJ_5_BINARY: @@ -405,8 +406,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); } - private static Optional toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { - switch (updateResultFw) { + private static Optional toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) { + switch (fwUpdateResult) { case INITIAL: return Optional.empty(); case UPDATE_SUCCESSFULLY: @@ -421,12 +422,12 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl case UNSUPPORTED_PROTOCOL: return Optional.of(FAILED); default: - throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); + throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", fwUpdateResult.name()); } } - private static Optional toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { - switch (updateStateFw) { + private static Optional toOtaPackageUpdateStatus(FirmwareUpdateState firmwareUpdateState) { + switch (firmwareUpdateState) { case IDLE: return Optional.empty(); case DOWNLOADING: @@ -436,7 +437,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl case UPDATING: return Optional.of(UPDATING); default: - throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw); + throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", firmwareUpdateState); } } @@ -444,10 +445,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static Optional toSwSateResultUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { - switch (updateResultSw) { + public static Optional toSwSateResultUpdateStatus(SoftwareUpdateState softwareUpdateState, SoftwareUpdateResult softwareUpdateResult) { + switch (softwareUpdateResult) { case INITIAL: - switch (updateStateSw) { + switch (softwareUpdateState) { case INITIAL: case DOWNLOAD_STARTED: return Optional.of(DOWNLOADING); @@ -473,10 +474,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl case UN_INSTALL_FAILURE: return Optional.of(FAILED); default: - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name()); + throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", softwareUpdateState.name(), softwareUpdateResult.name()); } } - - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java index 98e2055fdc..2b503ef8a4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -19,8 +19,8 @@ import lombok.Data; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateResultFw; -import org.thingsboard.server.transport.lwm2m.server.ota.firmware.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult; +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState; import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy; import java.util.Optional; @@ -47,8 +47,8 @@ public class LwM2MClientOtaInfo { //TODO: use value from device if applicable; private LwM2MFirmwareUpdateStrategy fwStrategy; private LwM2MSoftwareUpdateStrategy swStrategy; - private UpdateStateFw updateState; - private UpdateResultFw updateResult; + private FirmwareUpdateState updateState; + private FirmwareUpdateResult updateResult; private String failedPackageId; private int retryAttempts; @@ -90,7 +90,7 @@ public class LwM2MClientOtaInfo { return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3); } - public void setUpdateResult(UpdateResultFw updateResult) { + public void setUpdateResult(FirmwareUpdateResult updateResult) { this.updateResult = updateResult; switch (updateResult) { case INITIAL: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index 644099ee9d..3ba1b1500a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota; +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import java.util.Optional; @@ -31,9 +32,9 @@ public interface LwM2MOtaUpdateService { void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); - void onCurrentFirmwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl); + void onCurrentFirmwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration); - void onCurrentSoftwareStrategyUpdate(LwM2mClient client, Integer newStrategy, String newBaseUrl); + void onCurrentSoftwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration); void onCurrentFirmwareVersion3Update(LwM2mClient client, String version); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareDeliveryMethod.java similarity index 70% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareDeliveryMethod.java index dcb93a5283..4df6367d6f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateDeliveryFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareDeliveryMethod.java @@ -15,14 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota.firmware; -/** - * /** Delivery Method R - * 0: Pull only - * 1: Push only - * 2: Both: - * - In this case the LwM2M Server MAY choose the preferred mechanism for conveying the firmware image to the LwM2M Client. - */ -public enum UpdateDeliveryFw { +public enum FirmwareDeliveryMethod { PULL(0, "Pull only"), PUSH(1, "Push only"), BOTH(2, "Push or Push"); @@ -30,13 +23,13 @@ public enum UpdateDeliveryFw { public int code; public String type; - UpdateDeliveryFw(int code, String type) { + FirmwareDeliveryMethod(int code, String type) { this.code = code; this.type = type; } - public static UpdateDeliveryFw fromStateFwByType(String type) { - for (UpdateDeliveryFw to : UpdateDeliveryFw.values()) { + public static FirmwareDeliveryMethod fromStateFwByType(String type) { + for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) { if (to.type.equals(type)) { return to; } @@ -44,8 +37,8 @@ public enum UpdateDeliveryFw { throw new IllegalArgumentException(String.format("Unsupported FW delivery type : %s", type)); } - public static UpdateDeliveryFw fromStateFwByCode(int code) { - for (UpdateDeliveryFw to : UpdateDeliveryFw.values()) { + public static FirmwareDeliveryMethod fromStateFwByCode(int code) { + for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) { if (to.code == code) { return to; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateResult.java similarity index 86% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateResult.java index 5a71f41bc5..cdb7d71f25 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateResultFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateResult.java @@ -30,7 +30,7 @@ import lombok.Getter; * 8: Firmware update failed. * 9: Unsupported protocol. */ -public enum UpdateResultFw { +public enum FirmwareUpdateResult { INITIAL(0, "Initial value", false), UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false), NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false), @@ -49,14 +49,14 @@ public enum UpdateResultFw { @Getter private boolean again; - UpdateResultFw(int code, String type, boolean isAgain) { + FirmwareUpdateResult(int code, String type, boolean isAgain) { this.code = code; this.type = type; this.again = isAgain; } - public static UpdateResultFw fromUpdateResultFwByType(String type) { - for (UpdateResultFw to : UpdateResultFw.values()) { + public static FirmwareUpdateResult fromUpdateResultFwByType(String type) { + for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) { if (to.type.equals(type)) { return to; } @@ -64,8 +64,8 @@ public enum UpdateResultFw { throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type)); } - public static UpdateResultFw fromUpdateResultFwByCode(int code) { - for (UpdateResultFw to : UpdateResultFw.values()) { + public static FirmwareUpdateResult fromUpdateResultFwByCode(int code) { + for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) { if (to.code == code) { return to; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateState.java similarity index 80% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateState.java index 447749fd50..ddd04fbfd0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/UpdateStateFw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateState.java @@ -22,7 +22,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota.firmware; * 2: Downloaded * 3: Updating */ -public enum UpdateStateFw { +public enum FirmwareUpdateState { IDLE(0, "Idle"), DOWNLOADING(1, "Downloading"), DOWNLOADED(2, "Downloaded"), @@ -31,13 +31,13 @@ public enum UpdateStateFw { public int code; public String type; - UpdateStateFw(int code, String type) { + FirmwareUpdateState(int code, String type) { this.code = code; this.type = type; } - public static UpdateStateFw fromStateFwByType(String type) { - for (UpdateStateFw to : UpdateStateFw.values()) { + public static FirmwareUpdateState fromStateFwByType(String type) { + for (FirmwareUpdateState to : FirmwareUpdateState.values()) { if (to.type.equals(type)) { return to; } @@ -45,8 +45,8 @@ public enum UpdateStateFw { throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); } - public static UpdateStateFw fromStateFwByCode(int code) { - for (UpdateStateFw to : UpdateStateFw.values()) { + public static FirmwareUpdateState fromStateFwByCode(int code) { + for (FirmwareUpdateState to : FirmwareUpdateState.values()) { if (to.code == code) { return to; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateResult.java similarity index 89% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateResult.java index 22c92205d0..e99d4e4aca 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateResultSw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateResult.java @@ -39,7 +39,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota.software; * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features) * This Resource MAY be reported by sending Observe operation. */ -public enum UpdateResultSw { +public enum SoftwareUpdateResult { INITIAL(0, "Initial value", false), DOWNLOADING(1, "Downloading", false), SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false), @@ -58,14 +58,14 @@ public enum UpdateResultSw { public String type; public boolean isAgain; - UpdateResultSw(int code, String type, boolean isAgain) { + SoftwareUpdateResult(int code, String type, boolean isAgain) { this.code = code; this.type = type; this.isAgain = isAgain; } - public static UpdateResultSw fromUpdateResultSwByType(String type) { - for (UpdateResultSw to : UpdateResultSw.values()) { + public static SoftwareUpdateResult fromUpdateResultSwByType(String type) { + for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) { if (to.type.equals(type)) { return to; } @@ -73,8 +73,8 @@ public enum UpdateResultSw { throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type)); } - public static UpdateResultSw fromUpdateResultSwByCode(int code) { - for (UpdateResultSw to : UpdateResultSw.values()) { + public static SoftwareUpdateResult fromUpdateResultSwByCode(int code) { + for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) { if (to.code == code) { return to; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateState.java similarity index 84% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateState.java index a5f772a11d..394d2e501a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/UpdateStateSw.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/SoftwareUpdateState.java @@ -26,7 +26,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota.software; * After executing the UnInstall Resource, the state changes to INITIAL. * 4: INSTALLED */ -public enum UpdateStateSw { +public enum SoftwareUpdateState { INITIAL(0, "Initial"), DOWNLOAD_STARTED(1, "DownloadStarted"), DOWNLOADED(2, "Downloaded"), @@ -36,13 +36,13 @@ public enum UpdateStateSw { public int code; public String type; - UpdateStateSw(int code, String type) { + SoftwareUpdateState(int code, String type) { this.code = code; this.type = type; } - public static UpdateStateSw fromUpdateStateSwByType(String type) { - for (UpdateStateSw to : UpdateStateSw.values()) { + public static SoftwareUpdateState fromUpdateStateSwByType(String type) { + for (SoftwareUpdateState to : SoftwareUpdateState.values()) { if (to.type.equals(type)) { return to; } @@ -50,8 +50,8 @@ public enum UpdateStateSw { throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type)); } - public static UpdateStateSw fromUpdateStateSwByCode(int code) { - for (UpdateStateSw to : UpdateStateSw.values()) { + public static SoftwareUpdateState fromUpdateStateSwByCode(int code) { + for (SoftwareUpdateState to : SoftwareUpdateState.values()) { if (to.code == code) { return to; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 3307db2fca..767c316cc6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -40,7 +40,9 @@ import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.ota.OtaPackageUtil; @@ -130,16 +132,13 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl private final LwM2mTransportContext context; private final LwM2MAttributesService attributesService; private final LwM2MOtaUpdateService otaService; - public final LwM2MTransportServerConfig config; + private final LwM2MTransportServerConfig config; private final LwM2MTelemetryLogService logService; - public final OtaPackageDataCache otaPackageDataCache; - public final LwM2mTransportServerHelper helper; + private final LwM2mTransportServerHelper helper; private final TbLwM2MDtlsSessionStore sessionStore; - public final LwM2mClientContext clientContext; + private final LwM2mClientContext clientContext; private final LwM2MRpcRequestHandler rpcHandler; - public final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; - - public final Map firmwareUpdateState; + private final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, @@ -150,7 +149,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @Lazy LwM2MAttributesService attributesService, @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, - OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.attributesService = attributesService; @@ -161,9 +159,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl this.logService = logService; this.rpcHandler = rpcHandler; this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; - this.otaPackageDataCache = otaPackageDataCache; this.context = context; - this.firmwareUpdateState = new ConcurrentHashMap<>(); this.sessionStore = sessionStore; } @@ -781,27 +777,19 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl } } - // # 7.1 // update value in fwInfo - if (!newProfile.getClientLwM2mSettings().getFwUpdateStrategy().equals(oldProfile.getClientLwM2mSettings().getFwUpdateStrategy()) - || (LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == newProfile.getClientLwM2mSettings().getFwUpdateStrategy() && - !newProfile.getClientLwM2mSettings().getFwUpdateRecourse().equals(oldProfile.getClientLwM2mSettings().getFwUpdateRecourse()))) { - clients.forEach(lwM2MClient -> { - otaService.onCurrentFirmwareStrategyUpdate(lwM2MClient, - newProfile.getClientLwM2mSettings().getFwUpdateStrategy(), - newProfile.getClientLwM2mSettings().getFwUpdateRecourse()); - }); + OtherConfiguration newLwM2mSettings = newProfile.getClientLwM2mSettings(); + OtherConfiguration oldLwM2mSettings = oldProfile.getClientLwM2mSettings(); + if (!newLwM2mSettings.getFwUpdateStrategy().equals(oldLwM2mSettings.getFwUpdateStrategy()) + || (StringUtils.isNotEmpty(newLwM2mSettings.getFwUpdateRecourse()) && + !newLwM2mSettings.getFwUpdateRecourse().equals(oldLwM2mSettings.getFwUpdateRecourse()))) { + clients.forEach(lwM2MClient -> otaService.onCurrentFirmwareStrategyUpdate(lwM2MClient, newLwM2mSettings)); } - //# 7.2 // update value in swInfo - if (!newProfile.getClientLwM2mSettings().getSwUpdateStrategy().equals(oldProfile.getClientLwM2mSettings().getSwUpdateStrategy()) - || (LwM2MSoftwareUpdateStrategy.TEMP_URL.code == newProfile.getClientLwM2mSettings().getSwUpdateStrategy() && - !newProfile.getClientLwM2mSettings().getSwUpdateRecourse().equals(oldProfile.getClientLwM2mSettings().getSwUpdateRecourse()))) { - clients.forEach(lwM2MClient -> { - otaService.onCurrentSoftwareStrategyUpdate(lwM2MClient, - newProfile.getClientLwM2mSettings().getFwUpdateStrategy(), - newProfile.getClientLwM2mSettings().getFwUpdateRecourse()); - }); + if (!newLwM2mSettings.getSwUpdateStrategy().equals(oldLwM2mSettings.getSwUpdateStrategy()) + || (StringUtils.isNotEmpty(newLwM2mSettings.getSwUpdateRecourse()) && + !newLwM2mSettings.getSwUpdateRecourse().equals(oldLwM2mSettings.getSwUpdateRecourse()))) { + clients.forEach(lwM2MClient -> otaService.onCurrentSoftwareStrategyUpdate(lwM2MClient, newLwM2mSettings)); } } } From a44bc1ba93589e5df89a900adc8e63c9f026f4d4 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 22 Jun 2021 16:21:56 +0300 Subject: [PATCH 114/151] added SendPendingRPC transport msg --- .../device/DeviceActorMessageProcessor.java | 21 ++++++++++++------- common/queue/src/main/proto/queue.proto | 14 ++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index a1458fd5ac..2d5f107d61 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -322,29 +322,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) { TransportToDeviceActorMsg msg = wrapper.getMsg(); TbCallback callback = wrapper.getCallback(); + var sessionInfo = msg.getSessionInfo(); + if (msg.hasSessionEvent()) { - processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); + processSessionStateMsgs(sessionInfo, msg.getSessionEvent()); } if (msg.hasSubscribeToAttributes()) { - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes()); + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes()); } if (msg.hasSubscribeToRPC()) { - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC()); + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC()); + } + if (msg.hasSendPendingRPC()) { + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo); } if (msg.hasGetAttributes()) { - handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes()); } if (msg.hasToDeviceRPCCallResponse()) { - processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse()); } if (msg.hasSubscriptionInfo()) { - handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo()); } if (msg.hasClaimDevice()) { - handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice()); + handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice()); } if (msg.hasPersistedRpcResponseMsg()) { - processPersistedRpcResponses(context, msg.getSessionInfo(), msg.getPersistedRpcResponseMsg()); + processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg()); } callback.onSuccess(); } diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index a7edd42142..d47e6838d3 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -318,6 +318,9 @@ message SubscribeToRPCMsg { SessionType sessionType = 2; } +message SendPendingRPCMsg { +} + message ToDeviceRpcRequestMsg { int32 requestId = 1; string methodName = 2; @@ -440,11 +443,12 @@ message TransportToDeviceActorMsg { GetAttributeRequestMsg getAttributes = 3; SubscribeToAttributeUpdatesMsg subscribeToAttributes = 4; SubscribeToRPCMsg subscribeToRPC = 5; - ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; - SubscriptionInfoProto subscriptionInfo = 7; - ClaimDeviceMsg claimDevice = 8; - ProvisionDeviceRequestMsg provisionDevice = 9; - ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10; + SendPendingRPCMsg sendPendingRPC = 6; + ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 7; + SubscriptionInfoProto subscriptionInfo = 8; + ClaimDeviceMsg claimDevice = 9; + ProvisionDeviceRequestMsg provisionDevice = 10; + ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 11; } message TransportToRuleEngineMsg { From 74d2a3ebaf124d5a3be07055229b697c06647226 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 22 Jun 2021 16:56:26 +0300 Subject: [PATCH 115/151] UI: Refactoring LWM2M device profile transport configuration Servers tab --- .../app/core/http/device-profile.service.ts | 42 ++++++++++++--- ...ofile-transport-configuration.component.ts | 5 +- .../lwm2m-device-config-server.component.html | 26 ++++++++- .../lwm2m-device-config-server.component.ts | 54 ++++++------------- ...ile-transport-configuration.component.html | 41 ++++++++++---- ...ofile-transport-configuration.component.ts | 46 ++++++++++++---- .../lwm2m/lwm2m-profile-config.models.ts | 44 ++++++--------- .../assets/locale/locale.constant-en_US.json | 16 ++++-- 8 files changed, 171 insertions(+), 103 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 6107a0853e..26e68a2e88 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -21,18 +21,23 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { Observable, of, throwError } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; -import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; -import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { deepClone, isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { + ObjectLwM2M, + securityConfigMode, + ServerSecurityConfig, + ServerSecurityConfigInfo +} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; import { SortOrder } from '@shared/models/page/sort-order'; import { OtaPackageService } from '@core/http/ota-package.service'; -import { mergeMap, tap } from 'rxjs/operators'; +import { map, mergeMap, tap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DeviceProfileService { - private lwm2mBootstrapSecurityInfoInMemoryCache = new Map(); + private lwm2mBootstrapSecurityInfoInMemoryCache = new Map(); constructor( private http: HttpClient, @@ -60,12 +65,12 @@ export class DeviceProfileService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } - public getLwm2mBootstrapSecurityInfo(isBootstrapServer: boolean, config?: RequestConfig): Observable { + public getLwm2mBootstrapSecurityInfo(isBootstrapServer: boolean, config?: RequestConfig): Observable { const securityConfig = this.lwm2mBootstrapSecurityInfoInMemoryCache.get(isBootstrapServer); if (securityConfig) { return of(securityConfig); } else { - return this.http.get( + return this.http.get( `/api/lwm2m/deviceProfile/bootstrap/${isBootstrapServer}`, defaultHttpOptionsFromConfig(config) ).pipe( @@ -74,6 +79,31 @@ export class DeviceProfileService { } } + public getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrapServer: boolean, securityMode = securityConfigMode.NO_SEC, + config?: RequestConfig): Observable { + return this.getLwm2mBootstrapSecurityInfo(isBootstrapServer, config).pipe( + map(securityConfig => { + const serverSecurityConfigInfo = deepClone(securityConfig); + switch (securityMode) { + case securityConfigMode.PSK: + serverSecurityConfigInfo.port = serverSecurityConfigInfo.securityPort; + serverSecurityConfigInfo.host = serverSecurityConfigInfo.securityHost; + serverSecurityConfigInfo.serverPublicKey = ''; + break; + case securityConfigMode.RPK: + case securityConfigMode.X509: + serverSecurityConfigInfo.port = serverSecurityConfigInfo.securityPort; + serverSecurityConfigInfo.host = serverSecurityConfigInfo.securityHost; + break; + case securityConfigMode.NO_SEC: + serverSecurityConfigInfo.serverPublicKey = ''; + break; + } + return serverSecurityConfigInfo; + }) + ); + } + public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable> { return this.http.get>( `/api/resource/lwm2m/page${pageLink.toQuery()}`, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index 50b3bc801c..f5eb23e9e3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -89,9 +89,10 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu if (configuration) { delete configuration.type; } + this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); setTimeout(() => { - this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); - }); + this.deviceProfileTransportConfigurationFormGroup.updateValueAndValidity(); + }, 0); } private updateModel() { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index 78f08ce31a..3a8ac64658 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -35,19 +35,33 @@ {{ 'device-profile.lwm2m.server-port' | translate }} - + {{ 'device-profile.lwm2m.server-port-required' | translate }} + + {{ 'device-profile.lwm2m.server-port-pattern' | translate }} + + + {{ 'device-profile.lwm2m.server-port-range' | translate }} +
{{ 'device-profile.lwm2m.short-id' | translate }} - + {{ 'device-profile.lwm2m.short-id-required' | translate }} + + {{ 'device-profile.lwm2m.short-id-pattern' | translate }} + + + {{ 'device-profile.lwm2m.short-id-range' | translate }} + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} @@ -57,6 +71,10 @@ {{ 'device-profile.lwm2m.client-hold-off-time-required' | translate }} + + {{ 'device-profile.lwm2m.client-hold-off-time-pattern' | translate }} + {{ 'device-profile.lwm2m.account-after-timeout' | translate }} @@ -66,6 +84,10 @@ {{ 'device-profile.lwm2m.account-after-timeout-required' | translate }} + + {{ 'device-profile.lwm2m.account-after-timeout-pattern' | translate }} +
+
{{ 'device-profile.lwm2m.short-id' | translate }} - + {{ 'device-profile.lwm2m.short-id-required' | translate }} + + {{ 'device-profile.lwm2m.short-id-range' | translate }} + + + {{ 'device-profile.lwm2m.short-id-pattern' | translate }} + {{ 'device-profile.lwm2m.lifetime' | translate }} @@ -56,6 +63,10 @@ {{ 'device-profile.lwm2m.lifetime-required' | translate }} + + {{ 'device-profile.lwm2m.lifetime-pattern' | translate }} + {{ 'device-profile.lwm2m.default-min-period' | translate }} @@ -63,6 +74,10 @@ {{ 'device-profile.lwm2m.default-min-period-required' | translate }} + + {{ 'device-profile.lwm2m.default-min-period-pattern' | translate }} +
@@ -82,19 +97,23 @@ {{ 'device-profile.lwm2m.bootstrap-server' | translate }} - - + + + + {{ 'device-profile.lwm2m.lwm2m-server' | translate }} - - + + + + @@ -148,7 +167,7 @@ - + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index ad53bd987f..2d330620ef 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -29,7 +29,10 @@ import { DEFAULT_MIN_PERIOD, DEFAULT_NOTIF_IF_DESIBLED, DEFAULT_SW_UPDATE_RESOURCE, - getDefaultProfileConfig, + getDefaultBootstrapServerSecurityConfig, + getDefaultBootstrapServersSecurityConfig, getDefaultLwM2MServerSecurityConfig, + getDefaultProfileClientLwM2mSettingsConfig, + getDefaultProfileObserveAttrConfig, Instance, INSTANCES, KEY_NAME, @@ -38,7 +41,7 @@ import { ObjectLwM2M, OBSERVE, OBSERVE_ATTR_TELEMETRY, - RESOURCES, + RESOURCES, ServerSecurityConfig, TELEMETRY } from './lwm2m-profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; @@ -94,16 +97,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrap: this.fb.group({ servers: this.fb.group({ binding: [DEFAULT_BINDING], - shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(0)]], - lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0)]], + shortId: [DEFAULT_ID_SERVER, [Validators.required, Validators.min(1), Validators.max(65534), Validators.pattern('[0-9]*')]], + lifetime: [DEFAULT_LIFE_TIME, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], notifIfDisabled: [DEFAULT_NOTIF_IF_DESIBLED, []], - defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0)]], + defaultMinPeriod: [DEFAULT_MIN_PERIOD, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], }), bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required] }), clientLwM2mSettings: this.fb.group({ - clientStrategy: [1, []], + clientOnlyObserveAfterConnect: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], fwUpdateRecourse: [{value: '', disabled: true}, []], @@ -177,12 +180,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } - writeValue(value: Lwm2mProfileConfigModels | null): void { + async writeValue(value: Lwm2mProfileConfigModels | null) { if (isDefinedAndNotNull(value)) { - if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { + if (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap) { this.configurationValue = value; } else { - this.configurationValue = getDefaultProfileConfig(); + this.configurationValue = await this.defaultProfileConfig(); } this.lwm2mDeviceConfigFormGroup.patchValue({ configurationJson: this.configurationValue @@ -191,6 +194,29 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } } + private async defaultProfileConfig(): Promise { + let bootstrap: ServerSecurityConfig; + let lwm2m: ServerSecurityConfig; + try { + [bootstrap, lwm2m] = await Promise.all([ + this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(true).toPromise(), + this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(false).toPromise() + ]); + } catch (e) { + bootstrap = getDefaultBootstrapServerSecurityConfig(); + lwm2m = getDefaultLwM2MServerSecurityConfig(); + } + return { + observeAttr: getDefaultProfileObserveAttrConfig(), + bootstrap: { + servers: getDefaultBootstrapServersSecurityConfig(), + bootstrapServer: bootstrap, + lwm2mServer: lwm2m + }, + clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig() + }; + } + private initWriteValue = (): void => { const modelValue = {objectIds: [], objectsList: []} as ModelValue; modelValue.objectIds = this.getObjectsFromJsonAllConfig(); @@ -220,7 +246,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), bootstrap: this.configurationValue.bootstrap, clientLwM2mSettings: { - clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, fwUpdateRecourse: fwResource, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index a5d3502808..cbd981c4c8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -39,7 +39,7 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_X509 = 3000; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; -export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; +export const KEY_REGEXP_NUMBER = /^(-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://'; @@ -143,18 +143,20 @@ export interface BootstrapServersSecurityConfig { export interface ServerSecurityConfig { host?: string; - securityHost?: string; port?: number; - securityPort?: number; securityMode: securityConfigMode; - clientPublicKeyOrId?: string; - clientSecretKey?: string; serverPublicKey?: string; clientHoldOffTime?: number; serverId?: number; bootstrapServerAccountTimeout: number; } +export interface ServerSecurityConfigInfo extends ServerSecurityConfig { + securityHost?: string; + securityPort?: number; + bootstrapServerIs: boolean; +} + interface BootstrapSecurityConfig { servers: BootstrapServersSecurityConfig; bootstrapServer: ServerSecurityConfig; @@ -168,7 +170,7 @@ export interface Lwm2mProfileConfigModels { } export interface ClientLwM2mSettings { - clientStrategy: string; + clientOnlyObserveAfterConnect: number; fwUpdateStrategy: number; swUpdateStrategy: number; fwUpdateRecourse: string; @@ -193,9 +195,9 @@ export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecu }; } -export function getDefaultBootstrapServerSecurityConfig(hostname: string): ServerSecurityConfig { +export function getDefaultBootstrapServerSecurityConfig(): ServerSecurityConfig { return { - host: hostname, + host: DEFAULT_LOCAL_HOST_NAME, port: DEFAULT_PORT_BOOTSTRAP_NO_SEC, securityMode: securityConfigMode.NO_SEC, serverPublicKey: '', @@ -205,22 +207,14 @@ export function getDefaultBootstrapServerSecurityConfig(hostname: string): Serve }; } -export function getDefaultLwM2MServerSecurityConfig(hostname): ServerSecurityConfig { - const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(hostname); +export function getDefaultLwM2MServerSecurityConfig(): ServerSecurityConfig { + const DefaultLwM2MServerSecurityConfig = getDefaultBootstrapServerSecurityConfig(); DefaultLwM2MServerSecurityConfig.port = DEFAULT_PORT_SERVER_NO_SEC; DefaultLwM2MServerSecurityConfig.serverId = DEFAULT_ID_SERVER; return DefaultLwM2MServerSecurityConfig; } -function getDefaultProfileBootstrapSecurityConfig(hostname: any): BootstrapSecurityConfig { - return { - servers: getDefaultBootstrapServersSecurityConfig(), - bootstrapServer: getDefaultBootstrapServerSecurityConfig(hostname), - lwm2mServer: getDefaultLwM2MServerSecurityConfig(hostname) - }; -} - -function getDefaultProfileObserveAttrConfig(): ObservableAttributes { +export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { return { observe: [], attribute: [], @@ -230,17 +224,9 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes { }; } -export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModels { - return { - clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(), - observeAttr: getDefaultProfileObserveAttrConfig(), - bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_LOCAL_HOST_NAME) - }; -} - -function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { +export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientStrategy: '1', + clientOnlyObserveAfterConnect: 1, fwUpdateStrategy: 1, swUpdateStrategy: 1, fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3537a13a77..075c656c26 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1257,10 +1257,14 @@ "servers": "Servers", "short-id": "Short ID", "short-id-required": "Short ID is required.", - "lifetime": "LwM2M Client registration Lifetime", - "lifetime-required": "LwM2M Client registration Lifetime is required.", - "default-min-period": "Minimum Period between two notifications (sec)", - "default-min-period-required": "Minimum Period is required.", + "short-id-range": "Short ID should be in a range from 1 to 65534.", + "short-id-pattern": "Short ID must be a positive integer.", + "lifetime": "Client registration lifetime", + "lifetime-required": "Client registration lifetime is required.", + "lifetime-pattern": "Client registration lifetime must be a positive integer.", + "default-min-period": "Minimum period between two notifications (s)", + "default-min-period-required": "Minimum period is required.", + "default-min-period-pattern": "Minimum period must be a positive integer.", "notification-storing": "Notification storing when disabled or offline", "binding": "Binding", "binding-type": { @@ -1282,15 +1286,19 @@ "server-host-required": "Host is required.", "server-port": "Port", "server-port-required": "Port is required.", + "server-port-pattern": "Port must be a positive integer.", + "server-port-range": "Port should be in a range from 1 to 65535.", "server-public-key": "Server Public Key", "server-public-key-required": "Server Public Key is required.", "server-public-key-pattern": "Server Public Key must be hex decimal format.", "server-public-key-length": "Server Public Key must be {{ count }} characters.", "client-hold-off-time": "Hold Off Time", "client-hold-off-time-required": "Hold Off Time is required.", + "client-hold-off-time-pattern": "Hold Off Time must be a positive integer.", "client-hold-off-time-tooltip": "Client Hold Off Time for use with a Bootstrap-Server only", "account-after-timeout": "Account after the timeout", "account-after-timeout-required": "Account after the timeout is required.", + "account-after-timeout-pattern": "Account after the timeout must be a positive integer.", "account-after-timeout-tooltip": "Bootstrap-Server Account after the timeout value given by this resource.", "others-tab": "Other settings", "client-strategy": "Client strategy when connecting", From ac18585e17f41f0dc473b5a8400770b26fb61f6b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 22 Jun 2021 18:03:44 +0300 Subject: [PATCH 116/151] Coap: noSec one Endpoint --- .../server/coapserver/DefaultCoapServerService.java | 13 +------------ .../server/transport/coap/CoapTransportService.java | 11 ----------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 605952e456..43d33c8a82 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -27,8 +27,6 @@ import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Random; import java.util.concurrent.ConcurrentMap; @@ -87,12 +85,6 @@ public class DefaultCoapServerService implements CoapServerService { } private CoapServer createCoapServer() throws UnknownHostException { - server = new CoapServer(); - - CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder(); - InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); - InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); - noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); NetworkConfig networkConfig = new NetworkConfig(); networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); networkConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); @@ -102,10 +94,7 @@ public class DefaultCoapServerService implements CoapServerService { networkConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); networkConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); networkConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); - noSecCoapEndpointBuilder.setNetworkConfig(networkConfig); - CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); - server.addEndpoint(noSecCoapEndpoint); - + server = new CoapServer(networkConfig, coapServerContext.getPort()); if (isDtlsEnabled()) { CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder(); TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings(); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index a5adc36d7e..93096c901e 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.transport.coap; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapServer; -import org.eclipse.californium.core.network.config.NetworkConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.coapserver.CoapServerService; @@ -31,8 +30,6 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.net.UnknownHostException; -import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME; - @Service("CoapTransportService") @TbCoapServerComponent @Slf4j @@ -55,14 +52,6 @@ public class CoapTransportService implements TbTransportService { public void init() throws UnknownHostException { log.info("Starting CoAP transport..."); coapServer = coapServerService.getCoapServer(); - coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); - coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); - coapServer.getConfig().setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME); - coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024); - coapServer.getConfig().setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); - coapServer.getConfig().setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); - coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); - coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); CoapResource api = new CoapResource(API); api.add(new CoapTransportResource(coapTransportContext, coapServerService, V1)); From daca44dbd871269cf6457cb93f1181bfb6014cfd Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 22 Jun 2021 18:08:02 +0300 Subject: [PATCH 117/151] revert TransportToDeviceActorMsg and added SendPendingRPCMsg to the end of the list. --- common/queue/src/main/proto/queue.proto | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index d47e6838d3..049b6934e7 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -443,12 +443,12 @@ message TransportToDeviceActorMsg { GetAttributeRequestMsg getAttributes = 3; SubscribeToAttributeUpdatesMsg subscribeToAttributes = 4; SubscribeToRPCMsg subscribeToRPC = 5; - SendPendingRPCMsg sendPendingRPC = 6; - ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 7; - SubscriptionInfoProto subscriptionInfo = 8; - ClaimDeviceMsg claimDevice = 9; - ProvisionDeviceRequestMsg provisionDevice = 10; - ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 11; + ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; + SubscriptionInfoProto subscriptionInfo = 7; + ClaimDeviceMsg claimDevice = 8; + ProvisionDeviceRequestMsg provisionDevice = 9; + ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10; + SendPendingRPCMsg sendPendingRPC = 11; } message TransportToRuleEngineMsg { From de4fa446f24b36141a4658ac5e3f1d0178c7ff68 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard Date: Tue, 22 Jun 2021 18:13:39 +0300 Subject: [PATCH 118/151] Correct class name --- .../{HasKey.java => AbstractLwM2MClientCredentialsWithKey.java} | 2 +- .../data/device/credentials/lwm2m/PSKClientCredentials.java | 2 +- .../data/device/credentials/lwm2m/RPKClientCredentials.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/{HasKey.java => AbstractLwM2MClientCredentialsWithKey.java} (91%) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/AbstractLwM2MClientCredentialsWithKey.java similarity index 91% rename from common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java rename to common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/AbstractLwM2MClientCredentialsWithKey.java index 58cfa84099..2a3c0ab434 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/HasKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/AbstractLwM2MClientCredentialsWithKey.java @@ -21,7 +21,7 @@ import lombok.Setter; import lombok.SneakyThrows; import org.apache.commons.codec.binary.Hex; -public abstract class HasKey extends AbstractLwM2MClientCredentials { +public abstract class AbstractLwM2MClientCredentialsWithKey extends AbstractLwM2MClientCredentials { @Getter @Setter private String key; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/PSKClientCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/PSKClientCredentials.java index 2566af7da8..f90e85ff4a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/PSKClientCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/PSKClientCredentials.java @@ -20,7 +20,7 @@ import lombok.Setter; @Getter @Setter -public class PSKClientCredentials extends HasKey { +public class PSKClientCredentials extends AbstractLwM2MClientCredentialsWithKey { private String identity; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/RPKClientCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/RPKClientCredentials.java index fe329558f8..4ebe2de71a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/RPKClientCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/credentials/lwm2m/RPKClientCredentials.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.common.data.device.credentials.lwm2m; -public class RPKClientCredentials extends HasKey { +public class RPKClientCredentials extends AbstractLwM2MClientCredentialsWithKey { @Override public LwM2MSecurityMode getSecurityConfigClientMode() { From d077ee6a07a29c2184ff1bbae3a3a5932bb6aa6a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 22 Jun 2021 18:59:19 +0300 Subject: [PATCH 119/151] Improved Security Store to support race conditions during registration --- .../lwm2m/X509LwM2MIntegrationTest.java | 6 +-- .../TbLwM2MDtlsCertificateVerifier.java | 6 +-- .../server/client/LwM2mClientContextImpl.java | 10 ++-- .../server/store/TbLwM2mSecurityStore.java | 49 ++++++++++++++----- .../server/store/TbLwM2mStoreFactory.java | 2 +- .../server/store/TbMainSecurityStore.java | 29 +++++++++++ 6 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbMainSecurityStore.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java index 661f7c5474..7b60414e9c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/X509LwM2MIntegrationTest.java @@ -75,13 +75,11 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { return device; } - //TODO: use different endpoints to isolate tests. - @Ignore() @Test public void testConnectAndObserveTelemetry() throws Exception { createDeviceProfile(TRANSPORT_CONFIGURATION); X509ClientCredentials credentials = new X509ClientCredentials(); - credentials.setEndpoint(endpoint+1); + credentials.setEndpoint(endpoint); Device device = createDevice(credentials); SingleEntityFilter sef = new SingleEntityFilter(); @@ -99,7 +97,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { wsClient.waitForReply(); wsClient.registerWaitForUpdate(); - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint+1); + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint); Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); client.init(security, coapConfig); String msg = wsClient.waitForUpdate(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index 792ba131e8..04b69c815f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -42,8 +42,8 @@ import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; import javax.annotation.PostConstruct; import javax.security.auth.x500.X500Principal; @@ -67,7 +67,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer private final TbLwM2MDtlsSessionStore sessionStorage; private final LwM2MTransportServerConfig config; private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; - private final TbEditableSecurityStore securityStore; + private final TbMainSecurityStore securityStore; @SuppressWarnings("deprecation") private StaticCertificateVerifier staticCertificateVerifier; @@ -134,7 +134,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer if (msg.hasDeviceInfo() && deviceProfile != null) { sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg)); try { - securityStore.put(securityInfo); + securityStore.putX509(securityInfo); } catch (NonUniqueSecurityInfoException e) { log.trace("Failed to add security info: {}", securityInfo, e); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index d60482a0c1..700a7aec78 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; @@ -30,7 +31,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; import java.util.Arrays; import java.util.Collection; @@ -54,7 +55,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; - private final TbEditableSecurityStore securityStore; + private final TbMainSecurityStore securityStore; private final Map lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map profiles = new ConcurrentHashMap<>(); @@ -75,6 +76,9 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { oldSession = lwM2MClient.getSession(); TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); if (securityInfo.getSecurityMode() != null) { + if (SecurityMode.X509.equals(securityInfo.getSecurityMode())) { + securityStore.registerX509(registration.getEndpoint(), registration.getId()); + } if (securityInfo.getDeviceProfile() != null) { profileUpdate(securityInfo.getDeviceProfile()); if (securityInfo.getSecurityInfo() != null) { @@ -124,7 +128,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { if (currentRegistration.getId().equals(registration.getId())) { lwM2MClient.setState(LwM2MClientState.UNREGISTERED); lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint()); - this.securityStore.remove(lwM2MClient.getEndpoint()); + this.securityStore.remove(lwM2MClient.getEndpoint(), registration.getId()); UUID profileId = lwM2MClient.getProfileId(); if (profileId != null) { Optional otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java index d47be49978..bf1f275f32 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java @@ -22,13 +22,22 @@ import org.jetbrains.annotations.Nullable; import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; @Slf4j -public class TbLwM2mSecurityStore implements TbEditableSecurityStore { +public class TbLwM2mSecurityStore implements TbMainSecurityStore { private final TbEditableSecurityStore securityStore; private final LwM2mCredentialsSecurityInfoValidator validator; + private final ConcurrentMap> endpointRegistrations = new ConcurrentHashMap<>(); public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) { this.securityStore = securityStore; @@ -61,24 +70,42 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { @Nullable public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) { TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT); - try { - if (securityInfo != null) { + doPut(securityInfo); + return securityInfo != null ? securityInfo.getSecurityInfo() : null; + } + + private void doPut(TbLwM2MSecurityInfo securityInfo) { + if (securityInfo != null) { + try { securityStore.put(securityInfo); + } catch (NonUniqueSecurityInfoException e) { + log.trace("Failed to add security info: {}", securityInfo, e); } - } catch (NonUniqueSecurityInfoException e) { - log.trace("Failed to add security info: {}", securityInfo, e); } - return securityInfo != null ? securityInfo.getSecurityInfo() : null; } @Override - public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException { - securityStore.put(tbSecurityInfo); + public void putX509(TbLwM2MSecurityInfo securityInfo) throws NonUniqueSecurityInfoException { + securityStore.put(securityInfo); } @Override - public void remove(String endpoint) { - //TODO: Make sure we delay removal of security store from endpoint due to reg/unreg race condition. -// securityStore.remove(endpoint); + public void registerX509(String endpoint, String registrationId) { + endpointRegistrations.computeIfAbsent(endpoint, ep -> new HashSet<>()).add(registrationId); + } + + @Override + public void remove(String endpoint, String registrationId) { + Set epRegistrationIds = endpointRegistrations.get(endpoint); + boolean shouldRemove; + if (epRegistrationIds == null) { + shouldRemove = true; + } else { + epRegistrationIds.remove(registrationId); + shouldRemove = epRegistrationIds.isEmpty(); + } + if (shouldRemove) { + securityStore.remove(endpoint); + } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java index 154de636de..b9eb865df5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mStoreFactory.java @@ -51,7 +51,7 @@ public class TbLwM2mStoreFactory { } @Bean - private TbEditableSecurityStore securityStore() { + private TbMainSecurityStore securityStore() { return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ? new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbMainSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbMainSecurityStore.java new file mode 100644 index 0000000000..f4394fb337 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbMainSecurityStore.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.store; + +import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; + +public interface TbMainSecurityStore extends TbSecurityStore { + + void putX509(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException; + + void registerX509(String endpoint, String registrationId); + + void remove(String endpoint, String registrationId); + +} From 69271305d80dbf3e6feba33754367b621372e228 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 22 Jun 2021 19:39:46 +0300 Subject: [PATCH 120/151] Fix typo in rest method --- .../src/main/java/org/thingsboard/rest/client/RestClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 2d0914c7d4..0c4b673d7f 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -2378,7 +2378,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { public void setUserCredentialsEnabled(UserId userId, boolean userCredentialsEnabled) { restTemplate.postForLocation( - baseURL + "/api/user/{userId}/userCredentialsEnabled?serCredentialsEnabled={serCredentialsEnabled}", + baseURL + "/api/user/{userId}/userCredentialsEnabled?userCredentialsEnabled={userCredentialsEnabled}", null, userId.getId(), userCredentialsEnabled); From 91863ac1c161140cc0489580d035012257327c73 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 23 Jun 2021 14:52:11 +0300 Subject: [PATCH 121/151] Coap: noSec one Endpoint/Sec one EndPoint host&port from yml --- .../coapserver/DefaultCoapServerService.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 43d33c8a82..3eebbf0d3c 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -27,6 +27,8 @@ import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Random; import java.util.concurrent.ConcurrentMap; @@ -94,11 +96,23 @@ public class DefaultCoapServerService implements CoapServerService { networkConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024); networkConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); networkConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); - server = new CoapServer(networkConfig, coapServerContext.getPort()); + networkConfig.setInt(NetworkConfig.Keys.COAP_PORT, coapServerContext.getPort()); + server = new CoapServer(networkConfig); + + CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder(); + InetAddress addr = InetAddress.getByName(coapServerContext.getHost()); + InetSocketAddress sockAddr = new InetSocketAddress(addr, coapServerContext.getPort()); + noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr); + + noSecCoapEndpointBuilder.setNetworkConfig(networkConfig); + CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build(); + server.addEndpoint(noSecCoapEndpoint); if (isDtlsEnabled()) { CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder(); TbCoapDtlsSettings dtlsSettings = coapServerContext.getDtlsSettings(); DtlsConnectorConfig dtlsConnectorConfig = dtlsSettings.dtlsConnectorConfig(); + networkConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT, dtlsConnectorConfig.getAddress().getPort()); + dtlsCoapEndpointBuilder.setNetworkConfig(networkConfig); DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig); dtlsCoapEndpointBuilder.setConnector(connector); CoapEndpoint dtlsCoapEndpoint = dtlsCoapEndpointBuilder.build(); From 66d4c3fd20bf7843030820a44c06635156a109bc Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard <77969531+AndrewVolosytnykhThingsboard@users.noreply.github.com> Date: Wed, 23 Jun 2021 14:54:27 +0300 Subject: [PATCH 122/151] Clean exception message without class name and path (#4758) * Clean exception message without class name and path * Correct message, improvents to text of poppup on deleting * Added log for except files * Correct license header --- .../service/resource/DefaultTbResourceService.java | 11 ++++++----- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index cd8aa88863..689b7fd9a9 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -86,7 +86,10 @@ public class DefaultTbResourceService implements TbResourceService { } else { throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText())); } - } catch (InvalidDDFFileException | IOException e) { + } catch (InvalidDDFFileException e) { + log.error("Failed to parse file {}", resource.getFileName(), e); + throw new DataValidationException("Failed to parse file " + resource.getFileName()); + } catch (IOException e) { throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL); } if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource, true) == null) { @@ -194,8 +197,7 @@ public class DefaultTbResourceService implements TbResourceService { if (isSave) { LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); resources.add(lwM2MResourceObserve); - } - else if (v.operations.isReadable()) { + } else if (v.operations.isReadable()) { LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); resources.add(lwM2MResourceObserve); } @@ -204,8 +206,7 @@ public class DefaultTbResourceService implements TbResourceService { instance.setResources(resources.toArray(LwM2mResourceObserve[]::new)); lwM2mObject.setInstances(new LwM2mInstance[]{instance}); return lwM2mObject; - } - else { + } else { return null; } } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 4af8c7a87a..6736c168b2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2362,7 +2362,7 @@ "delete-resource-text": "Be careful, after the confirmation the resource will become unrecoverable.", "delete-resource-title": "Are you sure you want to delete the resource '{{resourceTitle}}'?", "delete-resources-action-title": "Delete { count, plural, 1 {1 resource} other {# resources} }", - "delete-resources-text": "Be careful, after the confirmation all selected resources will be removed.", + "delete-resources-text": "Please note that the selected resources, even if they are used in device profiles, will be deleted.", "delete-resources-title": "Are you sure you want to delete { count, plural, 1 {1 resource} other {# resources} }?", "download": "Download resource", "drop-file": "Drop a resource file or click to select a file to upload.", From c9b83a4c88bfaafa3943232bd72266008c37767d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 23 Jun 2021 19:22:14 +0300 Subject: [PATCH 123/151] UI: Refactoring LWM2M device profile transport configuration attributes settings --- .../device/data/lwm2m/ObjectAttributes.java | 10 +- .../lwm2m-attributes-dialog.component.html | 15 +- .../lwm2m-attributes-dialog.component.ts | 48 ++-- .../lwm2m-attributes-key-list.component.html | 83 +++---- .../lwm2m-attributes-key-list.component.scss | 69 ++++++ .../lwm2m-attributes-key-list.component.ts | 224 +++++++++--------- .../lwm2m/lwm2m-attributes.component.html | 10 +- .../lwm2m/lwm2m-attributes.component.scss | 52 +--- .../lwm2m/lwm2m-attributes.component.ts | 80 +++---- ...rve-attr-telemetry-resource.component.html | 22 +- ...wm2m-observe-attr-telemetry.component.html | 13 +- .../lwm2m/lwm2m-profile-config.models.ts | 72 +++--- .../assets/locale/locale.constant-en_US.json | 38 +-- 13 files changed, 381 insertions(+), 355 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java index ee54a23a07..7236774bcc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java @@ -24,10 +24,10 @@ public class ObjectAttributes { private Long dim; private String ver; - private Long pmin; - private Long pmax; - private Double gt; - private Double lt; - private Double st; + private Integer pmin; + private Integer pmax; + private Float gt; + private Float lt; + private Float st; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html index 5599e8d116..fbedbb5f76 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html @@ -15,12 +15,11 @@ limitations under the License. --> -
+ -
-

{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' : - 'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}

-
+

+ {{ (readonly ? 'device-profile.lwm2m.view-attributes' : 'device-profile.lwm2m.edit-attributes') | translate : {name: name} }} +

diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts index c1877e218e..6c674febf5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, Inject, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { DialogComponent } from '@shared/components/dialog.component'; import { Store } from '@ngrx/store'; @@ -22,12 +22,13 @@ import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; -import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; +import { AttributesNameValueMap } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; export interface Lwm2mAttributesDialogData { readonly: boolean; - attributeLwm2m: JsonObject; - destName: string; + attributes: AttributesNameValueMap; + modelName: string; + isResource: boolean; } @Component({ @@ -36,42 +37,37 @@ export interface Lwm2mAttributesDialogData { styleUrls: ['./lwm2m-attributes.component.scss'], providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], }) -export class Lwm2mAttributesDialogComponent extends DialogComponent - implements OnInit, ErrorStateMatcher { +export class Lwm2mAttributesDialogComponent + extends DialogComponent implements ErrorStateMatcher { - readonly = this.data.readonly; - - attributeLwm2m = this.data.attributeLwm2m; - - submitted = false; + readonly: boolean; + name: string; + isResource: boolean; - dirtyValue = false; + private submitted = false; - attributeLwm2mDialogFormGroup: FormGroup; + attributeFormGroup: FormGroup; constructor(protected store: Store, protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData, + @Inject(MAT_DIALOG_DATA) private data: Lwm2mAttributesDialogData, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private fb: FormBuilder) { super(store, router, dialogRef); - this.attributeLwm2mDialogFormGroup = this.fb.group({ - keyFilters: [{}, []] - }); - this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m}); - this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => { - this.attributeLwm2m = attributes; + this.readonly = data.readonly; + this.name = data.modelName; + this.isResource = data.isResource; + + this.attributeFormGroup = this.fb.group({ + attributes: [data.attributes] }); if (this.readonly) { - this.attributeLwm2mDialogFormGroup.disable({emitEvent: false}); + this.attributeFormGroup.disable({emitEvent: false}); } } - ngOnInit(): void { - } - isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { const originalErrorState = this.errorStateMatcher.isErrorState(control, form); const customErrorState = !!(control && control.invalid && this.submitted); @@ -80,7 +76,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent -
-
- device-profile.lwm2m.attribute-lwm2m-destination - {{ titleText }} -
+
- device-profile.lwm2m.attribute-lwm2m-name - device-profile.lwm2m.attribute-lwm2m-value -
+ + +
-
-
- +
+
+ - - - {{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }} + + + {{ attributeNameTranslationMap.get(attributeName) | translate }} + + {{ 'device-profile.lwm2m.attribute-name-required' | translate }} + - + - + + + {{ 'device-profile.lwm2m.attribute-value-required' | translate }} + + + {{ 'device-profile.lwm2m.attribute-value-pattern' | translate }} + -
- - {{ 'device-profile.lwm2m.key-name' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - {{ 'device-profile.lwm2m.valid-attribute-lwm2m-key' | translate: {attrEnums: attrKeys} }} - - - {{ 'device-profile.lwm2m.valid-attribute-lwm2m-value' | translate: {attrEnums: attrKeys} }} -
- {{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}} -
-
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss new file mode 100644 index 0000000000..23515e87bc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .name-value-map { + span.no-data-found { + position: relative; + display: flex; + height: 40px; + + &.disabled { + color: rgba(0, 0, 0, .38); + } + } + + .map-list{ + height: 45px; + } + } +} + +:host ::ng-deep { + .mat-form-field-wrapper { + padding-bottom: 0; + } + .mat-form-field-infix { + border-top: 0; + } + .mat-form-field-underline { + bottom: 0; + } + + .button-icon{ + font-size: 20px; + width: 20px; + height: 20px; + } + + .map-list { + mat-form-field { + .mat-form-field-wrapper { + padding-bottom: 0; + .mat-form-field-infix { + border-top-width: 0.2em; + width: auto; + min-width: auto; + } + .mat-form-field-underline { + bottom: 0; + } + .mat-form-field-subscript-wrapper{ + margin-top: 1.8em; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts index 686382e9bd..f84a0511c6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts @@ -14,35 +14,36 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { AbstractControl, ControlValueAccessor, FormArray, FormBuilder, - FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator, Validators } from '@angular/forms'; -import { Subscription } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { - ATTRIBUTE_KEYS, - ATTRIBUTE_LWM2M_ENUM, - ATTRIBUTE_LWM2M_MAP + AttributeName, + AttributeNameTranslationMap, + AttributesNameValue, + AttributesNameValueMap, + valueValidatorByAttributeName } from './lwm2m-profile-config.models'; -import { isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull } from '@core/utils'; +import { isUndefinedOrNull } from '@core/utils'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; - +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-lwm2m-attributes-key-list', templateUrl: './lwm2m-attributes-key-list.component.html', - styleUrls: ['./lwm2m-attributes.component.scss'], + styleUrls: ['./lwm2m-attributes-key-list.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -56,39 +57,46 @@ import { PageComponent } from '@shared/components/page.component'; } ] }) -export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { - - attrKeys = ATTRIBUTE_KEYS; - - attrKey = ATTRIBUTE_LWM2M_ENUM; +export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnDestroy, OnDestroy, Validator { - attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP; + attributeNames; + attributeNameTranslationMap = AttributeNameTranslationMap; @Input() disabled: boolean; - @Input() titleText: string; + @Input() + isResource = false; - @Input() noDataText: string; - - kvListFormGroup: FormGroup; + attributesValueFormGroup: FormGroup; private propagateChange = null; - - private valueChangeSubscription: Subscription = null; + private valueChange$: Subscription = null; + private destroy$ = new Subject(); + private usedAttributesName: AttributeName[] = []; constructor(protected store: Store, private fb: FormBuilder) { super(store); + this.attributesValueFormGroup = this.fb.group({ + attributesValue: this.fb.array([]) + }); } - ngOnInit(): void { - this.kvListFormGroup = this.fb.group({}); - this.kvListFormGroup.addControl('keyVals', - this.fb.array([])); + ngOnInit() { + if (this.isResource) { + this.attributeNames = Object.values(AttributeName); + } else { + this.attributeNames = Object.values(AttributeName) + .filter(item => ![AttributeName.lt, AttributeName.gt, AttributeName.st].includes(item)); + } } - keyValsFormArray(): FormArray { - return this.kvListFormGroup.get('keyVals') as FormArray; + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + this.destroy$.next(); + this.destroy$.complete(); } registerOnChange(fn: any): void { @@ -101,127 +109,111 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { - this.kvListFormGroup.disable({emitEvent: false}); + this.attributesValueFormGroup.disable({emitEvent: false}); } else { - this.kvListFormGroup.enable({emitEvent: false}); + this.attributesValueFormGroup.enable({emitEvent: false}); } } - writeValue(keyValMap: { [key: string]: string }): void { - if (this.valueChangeSubscription) { - this.valueChangeSubscription.unsubscribe(); + writeValue(keyValMap: AttributesNameValueMap): void { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); } - const keyValsControls: Array = []; + const attributesValueControls: Array = []; if (keyValMap) { - for (const property of Object.keys(keyValMap)) { - if (Object.prototype.hasOwnProperty.call(keyValMap, property)) { - keyValsControls.push(this.fb.group({ - key: [property, [Validators.required, this.attributeLwm2mKeyValidator]], - value: [keyValMap[property], this.attributeLwm2mValueValidator(property)] - })); - } - } + (Object.keys(keyValMap) as AttributeName[]).forEach(name => { + attributesValueControls.push(this.createdFormGroup({name, value: keyValMap[name]})); + }); } - this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls)); - this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { - // this.updateValidate(); - this.updateModel(); - }); + this.attributesValueFormGroup.setControl('attributesValue', this.fb.array(attributesValueControls)); if (this.disabled) { - this.kvListFormGroup.disable({emitEvent: false}); + this.attributesValueFormGroup.disable({emitEvent: false}); } else { - this.kvListFormGroup.enable({emitEvent: false}); + this.attributesValueFormGroup.enable({emitEvent: false}); } + this.valueChange$ = this.attributesValueFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + this.updateUsedAttributesName(); + } + + attributesValueFormArray(): FormArray { + return this.attributesValueFormGroup.get('attributesValue') as FormArray; } public removeKeyVal(index: number) { - (this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index); + this.attributesValueFormArray().removeAt(index); } public addKeyVal() { - const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray; - keyValsFormArray.push(this.fb.group({ - key: ['', [Validators.required, this.attributeLwm2mKeyValidator]], - value: ['', []] - })); - } - - public validate(c?: FormControl) { - const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; - let valid = true; - for (const entry of kvList) { - if (isUndefinedOrNull(entry.key) || isEmptyStr(entry.key) || !ATTRIBUTE_KEYS.includes(entry.key)) { - valid = false; - break; - } - if (entry.key !== 'ver' && isNaN(Number(entry.value))) { - valid = false; - break; - } + this.attributesValueFormArray().push(this.createdFormGroup()); + this.attributesValueFormGroup.updateValueAndValidity({emitEvent: false}); + if (this.attributesValueFormGroup.invalid) { + this.updateModel(); } - return (valid) ? null : { - keyVals: { - valid: false, - }, - }; } - private updateValidate() { - const kvList = this.kvListFormGroup.get('keyVals') as FormArray; - kvList.controls.forEach(fg => { - if (fg.get('key').value === 'ver') { - fg.get('value').setValidators(null); - fg.get('value').setErrors(null); - } - else { - fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator); - fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value'))); - } + private createdFormGroup(value?: AttributesNameValue): FormGroup { + if (isUndefinedOrNull(value)) { + value = { + name: this.getFirstUnusedAttributesName(), + value: null + }; + } + const form = this.fb.group({ + name: [value.name, Validators.required], + value: [value.value, valueValidatorByAttributeName(value.name)] }); + form.get('name').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(name => { + form.get('value').setValidators(valueValidatorByAttributeName(name)); + form.get('value').updateValueAndValidity(); + }); + return form; + } + + public validate() { + return this.attributesValueFormGroup.valid ? null : { + attributesValue: { + valid: false + } + }; } private updateModel() { - this.updateValidate(); - if (this.validate() === null) { - const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; - const keyValMap: { [key: string]: string | number } = {}; - kvList.forEach((entry) => { - if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) { - keyValMap[entry.key] = entry.value.toString(); - } else { - keyValMap[entry.key] = Number(entry.value); - } - }); - this.propagateChange(keyValMap); - } - else { - this.propagateChange(null); - } + const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value; + const attributesNameValueMap: AttributesNameValueMap = {}; + value.forEach(attribute => { + attributesNameValueMap[attribute.name] = attribute.value; + }); + this.updateUsedAttributesName(); + this.propagateChange(attributesNameValueMap); } + public isDisabledAttributeName(type: AttributeName, index: number): boolean { + const usedIndex = this.usedAttributesName.indexOf(type); + return usedIndex > -1 && usedIndex !== index; + } - private attributeLwm2mKeyValidator = (control: AbstractControl) => { - const key = control.value as string; - if (isDefinedAndNotNull(key) && !isEmpty(key)) { - if (!ATTRIBUTE_KEYS.includes(key)) { - return { - validAttributeKey: true - }; + private getFirstUnusedAttributesName(): AttributeName { + for (const attributeName of this.attributeNames) { + if (this.usedAttributesName.indexOf(attributeName) === -1) { + return attributeName; } } return null; } - private attributeLwm2mValueNumberValidator = (control: AbstractControl) => { - if (isNaN(Number(control.value)) || Number(control.value) < 0) { - return { - validAttributeValue: true - }; - } - return null; + private updateUsedAttributesName() { + this.usedAttributesName = []; + const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value; + value.forEach((attributesValue, index) => { + this.usedAttributesName[index] = attributesValue.name; + }); } - private attributeLwm2mValueValidator = (property: string): object[] => { - return property === 'ver' ? [] : [this.attributeLwm2mValueNumberValidator]; + get isAddEnabled(): boolean { + return this.attributesValueFormArray().length !== this.attributeNames.length; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html index 2011cd65f5..b9e8c13ac6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html @@ -15,18 +15,14 @@ limitations under the License. --> -
-
- {{attributeLwm2mToString()}} -
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss index 63dd0bdb3e..b33df98d85 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss @@ -14,48 +14,20 @@ * limitations under the License. */ :host { - .tb-kv-map { - span.no-data-found { - position: relative; - display: flex; - height: 40px; - - &.disabled { - color: rgba(0, 0, 0, .38); - } - } + .resource-name-lw-end{ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align:end; + //width: 80px; + cursor: pointer; } -} -:host ::ng-deep { - .mat-form-field-wrapper { - padding-bottom: 0; - } - .mat-form-field-infix { - border-top: 0; + .resource-name-lw{ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + cursor: pointer; } - .mat-form-field-underline { - bottom: 0; - } -} - -.vertical-padding { - padding: 0 0 10px 20px; -} - -.resource-name-lw-end{ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-align:end; - //width: 80px; - cursor: pointer; -} - -.resource-name-lw{ - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - cursor: pointer; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts index 1e467bcd93..99d331d8a8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts @@ -17,11 +17,10 @@ import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils'; +import { isEmpty, isUndefinedOrNull } from '@core/utils'; import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; import { MatDialog } from '@angular/material/dialog'; -import { TranslateService } from '@ngx-translate/core'; -import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models'; +import { AttributesNameValueMap } from './lwm2m-profile-config.models'; @Component({ @@ -36,22 +35,21 @@ import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models'; }) export class Lwm2mAttributesComponent implements ControlValueAccessor { attributeLwm2mFormGroup: FormGroup; - attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL; private requiredValue: boolean; - @Input() - attributeLwm2m: {}; - @Input() isAttributeTelemetry: boolean; @Input() - destName: string; + modelName: string; @Input() disabled: boolean; + @Input() + isResource = false; + @Output() updateAttributeLwm2m = new EventEmitter(); @@ -64,8 +62,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { } constructor(private dialog: MatDialog, - private fb: FormBuilder, - private translate: TranslateService) {} + private fb: FormBuilder) {} registerOnChange(fn: any): void { this.propagateChange = fn; @@ -85,63 +82,66 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { ngOnInit() { this.attributeLwm2mFormGroup = this.fb.group({ - attributeLwm2m: [this.attributeLwm2m] + attributes: [{}] }); } - writeValue(value: {} | null): void {} - - attributeLwm2mToString = (): string => { - return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data'); + writeValue(value: AttributesNameValueMap | null) { + this.attributeLwm2mFormGroup.patchValue({attributes: value}, {emitEvent: false}); } - private attributeLwm2mLabelToString = (): string => { - let label = JSON.stringify(this.attributeLwm2m); - label = deepClone(label.replace('{', '')); - label = deepClone(label.replace('}', '')); - this.attributeLwm2mLabel.forEach((value: string, key: string) => { - const dest = '\"' + key + '\"\:'; - label = deepClone(label.replace(dest, value)); - }); - return label; + get attributesValueMap(): AttributesNameValueMap { + return this.attributeLwm2mFormGroup.get('attributes').value; } isDisableBtn(): boolean { - return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) && - !isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled; + return !this.disabled && this.isAttributeTelemetry; } - isIconView(): boolean { - return this.isAttributeTelemetry || this.disabled; + isEmpty(): boolean { + const value = this.attributesValueMap; + return isUndefinedOrNull(value) || isEmpty(value); } - isIconEditAdd(): boolean { - return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m); + get tooltipSetAttributesTelemetry(): string { + return this.isDisableBtn() ? 'device-profile.lwm2m.edit-attributes-select' : ''; } - isToolTipLabel(): string { - return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') : - this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') : - this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip'); + get tooltipButton(): string { + if (this.disabled) { + return 'device-profile.lwm2m.view-attribute'; + } else if (this.isEmpty()) { + return 'device-profile.lwm2m.add-attribute'; + } + return 'device-profile.lwm2m.edit-attribute'; + } + + get iconButton(): string { + if (this.disabled) { + return 'visibility'; + } else if (this.isEmpty()) { + return 'add'; + } + return 'edit'; } public editAttributesLwm2m = ($event: Event): void => { if ($event) { $event.stopPropagation(); } - this.dialog.open(Lwm2mAttributesDialogComponent, { + this.dialog.open(Lwm2mAttributesDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { readonly: this.disabled, - attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m), - destName: this.destName + attributes: this.attributesValueMap, + modelName: this.modelName, + isResource: this.isResource } }).afterClosed().subscribe((result) => { if (result) { - this.attributeLwm2m = result; - this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m}); - this.updateAttributeLwm2m.next(this.attributeLwm2m); + this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: result}); + this.updateAttributeLwm2m.next(result); } }); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html index 3ee9bffa27..8c6a8fab84 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html @@ -20,27 +20,24 @@ *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
-
+
device-profile.lwm2m.resource-label
-
+
device-profile.lwm2m.attribute-label
-
+
device-profile.lwm2m.telemetry-label
-
+
device-profile.lwm2m.observe-label
device-profile.lwm2m.key-name-label
-
- device-profile.lwm2m.attribute-lwm2m-label -
-
<{{resourceLwM2M.get('id').value}}> {{resourceLwM2M.get('name').value}}
@@ -65,7 +62,7 @@ matTooltipPosition="above">
- + {{ 'device-profile.lwm2m.key-name-label' | translate }} {{ 'device-profile.lwm2m.required' | translate }} -
+ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html index 02d594905d..ee60f144ca 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html @@ -26,17 +26,15 @@
- + - + -
- - - -
-
-
- {{'device-profile.lwm2m.instance-label' | translate}} <{{instances.get('id').value}}> -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
-
-
-
- - - - -
-
+ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss index afed0db1ff..c619a2b5ce 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss @@ -13,23 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-panel-title-height { - user-select: none; - min-height: 32px; -} - -.checkbox-padding { - padding-left: 22px; - text-align:center; -} - :host{ section { padding: 2px; } - .instance-list { - mat-expansion-panel-header { - color: inherit; - } + + .tb-panel-title-height { + user-select: none; + min-height: 32px; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 530679113c..e4d0d9acbd 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -14,38 +14,29 @@ /// limitations under the License. /// -import { Component, forwardRef, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { AbstractControl, ControlValueAccessor, FormArray, FormBuilder, FormGroup, + NG_VALIDATORS, NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { - ATTRIBUTE, - ATTRIBUTE_LWM2M, - CLIENT_LWM2M, - Instance, - INSTANCES, - ObjectLwM2M, - ResourceLwM2M, - RESOURCES, - TELEMETRY -} from './lwm2m-profile-config.models'; -import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils'; +import { Instance, ObjectLwM2M } from './lwm2m-profile-config.models'; +import { deepClone, isDefinedAndNotNull, isEqual } from '@core/utils'; import { MatDialog } from '@angular/material/dialog'; -import { TranslateService } from '@ngx-translate/core'; import { Lwm2mObjectAddInstancesData, Lwm2mObjectAddInstancesDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component'; import _ from 'lodash'; +import { Subscription } from 'rxjs'; @Component({ selector: 'tb-profile-lwm2m-observe-attr-telemetry', @@ -56,18 +47,20 @@ import _ from 'lodash'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), + multi: true } ] }) -export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor { +export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, OnDestroy, Validator { - private requiredValue: boolean; - - valuePrev = null as any; - observeAttrTelemetryFormGroup: FormGroup; - resources = RESOURCES; + modelsFormGroup: FormGroup; + private requiredValue: boolean; get required(): boolean { return this.requiredValue; } @@ -84,132 +77,103 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @Input() disabled: boolean; - constructor(private store: Store, - private fb: FormBuilder, + private valueChange$: Subscription = null; + private propagateChange = (v: any) => { }; + + constructor(private fb: FormBuilder, private dialog: MatDialog, - public translate: TranslateService) { - this.observeAttrTelemetryFormGroup = this.fb.group({ - [CLIENT_LWM2M]: this.fb.array([]) - }); - this.observeAttrTelemetryFormGroup.valueChanges.subscribe(value => { - if (isUndefined(this.disabled) || !this.disabled) { - this.propagateChangeState(value); - } + private cd: ChangeDetectorRef) { + this.modelsFormGroup = this.fb.group({ + models: this.fb.array([]) }); } - private propagateChange = (v: any) => { - }; + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + } registerOnChange(fn: any): void { this.propagateChange = fn; } - private propagateChangeState = (value: any): void => { - if (value) { - if (this.valuePrev === null) { - this.valuePrev = 'init'; - } else if (this.valuePrev === 'init') { - this.valuePrev = value; - } else if (JSON.stringify(value) !== JSON.stringify(this.valuePrev)) { - this.valuePrev = value; - if (this.observeAttrTelemetryFormGroup.valid) { - this.propagateChange(value); - } else { - this.propagateChange(null); - } - } - } - } - registerOnTouched(fn: any): void { } setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; - this.valuePrev = null; if (isDisabled) { - this.observeAttrTelemetryFormGroup.disable(); + this.modelsFormGroup.disable({emitEvent: false}); } else { - this.observeAttrTelemetryFormGroup.enable(); + this.modelsFormGroup.enable({emitEvent: false}); } } - writeValue(value: {}): void { + writeValue(value: ObjectLwM2M[]): void { if (isDefinedAndNotNull(value)) { - this.buildClientObjectsLwM2M(value[CLIENT_LWM2M]); + this.updateModels(value); } } - private buildClientObjectsLwM2M = (objectsLwM2M: ObjectLwM2M []): void => { - this.observeAttrTelemetryFormGroup.setControl(CLIENT_LWM2M, - this.createObjectsLwM2M(objectsLwM2M) - ); + validate(): ValidationErrors | null { + return this.modelsFormGroup.valid ? null : { + modelsFormGroup: false + }; } - private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => { - return this.fb.array(objectsLwM2M.map((objectLwM2M) => { - return this.fb.group({ - keyId: objectLwM2M.keyId, - name: objectLwM2M.name, - multiple: objectLwM2M.multiple, - mandatory: objectLwM2M.mandatory, - attributeLwm2m: objectLwM2M.attributeLwm2m, - instances: this.createInstanceLwM2M(objectLwM2M.instances) - }); - })); + get modelsFormArray(): FormArray { + return this.modelsFormGroup.get('models') as FormArray; } - private createInstanceLwM2M = (instancesLwM2M: Instance[]): FormArray => { - return this.fb.array(instancesLwM2M.map((instanceLwM2M) => { - return this.fb.group({ - id: instanceLwM2M.id, - attributeLwm2m: {value: instanceLwM2M.attributeLwm2m, disabled: this.disabled}, - resources: {value: instanceLwM2M.resources, disabled: this.disabled} + private updateModels(models: ObjectLwM2M[]) { + if (models.length === this.modelsFormArray.length) { + this.modelsFormArray.patchValue(models, {emitEvent: false}); + } else { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + } + const modelControls: Array = []; + models.forEach(model => { + modelControls.push(this.createModelFormGroup(model)); }); - })); - } - - get clientLwM2MFormArray(): FormArray { - return this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray; - } - - instancesLwm2mFormArray = (objectLwM2M: AbstractControl): FormArray => { - return objectLwM2M.get(INSTANCES) as FormArray; - } - - changeInstanceResourcesCheckBox = (value: boolean, instance: AbstractControl, type: string): void => { - const resources = deepClone(instance.get(RESOURCES).value as ResourceLwM2M[]); - resources.forEach(resource => resource[type] = value); - instance.get(RESOURCES).patchValue(resources); - this.propagateChange(this.observeAttrTelemetryFormGroup.value); - } - - private updateValidators = (): void => { - this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).setValidators(this.required ? Validators.required : []); - this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity(); + this.modelsFormGroup.setControl('models', this.fb.array(modelControls)); + if (this.disabled) { + this.modelsFormGroup.disable({emitEvent: false}); + } + this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => { + this.updateModel(value.models); + }); + } } - trackByParams = (index: number, element: any): number => { - return index; + private createModelFormGroup(objectLwM2M: ObjectLwM2M): FormGroup { + return this.fb.group({ + id: [objectLwM2M.id], + keyId: [objectLwM2M.keyId], + name: [objectLwM2M.name], + multiple: [objectLwM2M.multiple], + mandatory: [objectLwM2M.mandatory], + attributes: [objectLwM2M.attributes], + instances: [objectLwM2M.instances] + }); } - getIndeterminate = (instance: AbstractControl, type: string): boolean => { - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; - if (isDefinedAndNotNull(resources)) { - const checkedResource = resources.filter(resource => resource[type]); - return checkedResource.length !== 0 && checkedResource.length !== resources.length; + private updateModel = (value: ObjectLwM2M[]): void => { + if (value && this.modelsFormGroup.valid) { + this.propagateChange(value); + } else { + this.propagateChange(null); } - return false; } - getChecked = (instance: AbstractControl, type: string): boolean => { - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; - return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); + private updateValidators = (): void => { + this.modelsFormArray.setValidators(this.required ? Validators.required : []); + this.modelsFormArray.updateValueAndValidity(); } - getExpended = (objectLwM2M: AbstractControl): boolean => { - return this.instancesLwm2mFormArray(objectLwM2M).length === 1; + trackByParams = (index: number, objectLwM2M: ObjectLwM2M): number => { + return objectLwM2M.id; } /** @@ -230,137 +194,76 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor * Object Instance ID cnt_max = cnt_min = 1 (всегда есть один) */ - addInstances = ($event: Event, object: ObjectLwM2M): void => { + addInstances = ($event: Event, control: AbstractControl): void => { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - this.dialog.open(Lwm2mObjectAddInstancesDialogComponent, { + const object: ObjectLwM2M = control.value; + const instancesId: Set = this.instancesToSetId(object.instances); + this.dialog.open(Lwm2mObjectAddInstancesDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - instancesIds: this.instancesToSetId(object.instances), + instancesId: new Set(instancesId), objectName: object.name, - objectKeyId: object.keyId + objectId: object.id } }).afterClosed().subscribe( - (res: Lwm2mObjectAddInstancesData | undefined) => { + (res: Set | null) => { if (isDefinedAndNotNull(res)) { - this.updateInstancesIds(res); + this.updateInstancesIds(res, control, instancesId); } } ); } - private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls - .find(e => e.value.keyId === data.objectKeyId) as FormGroup; - const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; - const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray; - const instance0 = deepClone(instancesFormArray.at(0).value as Instance); - instance0.resources.forEach(r => { - r.attribute = false; - r.telemetry = false; - r.observe = false; - r.attributeLwm2m = {}; + private updateInstancesIds(instancesId: Set, control: AbstractControl, prevInstancesId: Set) { + let instancesValue: Instance[] = control.get('instances').value; + const instanceTemplate = deepClone(instancesValue[0]); + instanceTemplate.attributes = {}; + instanceTemplate.resources.forEach(resource => { + resource.attribute = false; + resource.telemetry = false; + resource.observe = false; + resource.keyName = _.camelCase(resource.name); + resource.attributes = {}; }); - const valueOld = this.instancesToSetId(instancesArray); - if (!isEqual(valueOld, data.instancesIds)) { - const idsDel = this.diffBetweenSet(valueOld, data.instancesIds); - const idsAdd = this.diffBetweenSet(data.instancesIds, valueOld); + if (!isEqual(prevInstancesId, instancesId)) { + const idsDel = this.diffBetweenSet(prevInstancesId, instancesId); + const idsAdd = this.diffBetweenSet(instancesId, prevInstancesId); if (idsAdd.size) { - this.addInstancesNew(idsAdd, objectLwM2MFormGroup, instancesFormArray, instance0); + idsAdd.forEach(id => { + const template = deepClone(instanceTemplate); + template.resources.forEach(resource => resource.keyName += id); + template.id = id; + instancesValue.push(template); + }); } if (idsDel.size) { - this.deleteInstances(idsDel, objectLwM2MFormGroup, instancesFormArray, instance0); + instancesValue = instancesValue.filter(instance => !idsDel.has(instance.id)); + if (instancesValue.length === 0) { + instanceTemplate.id = 0; + instancesValue.push(instanceTemplate); + } + } + if (idsAdd.size || idsDel.size) { + instancesValue.sort((a, b) => a.id - b.id); + control.get('instances').patchValue(instancesValue); + this.cd.markForCheck(); } } } - private addInstancesNew = (idsAdd: Set, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, - instanceNew: Instance): void => { - idsAdd.forEach(x => { - instanceNew.resources.forEach(resource => {resource.keyName = _.camelCase(resource.name + x);}); - this.pushInstance(instancesFormArray, x, deepClone(instanceNew as Instance)); - }); - (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); - } - - private deleteInstances = (idsDel: Set, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, - instance0: Instance): void => { - idsDel.forEach(x => { - const instanceIndex = instancesFormArray.value.findIndex(element => element.id === x); - instancesFormArray.removeAt(instanceIndex); - }); - if (instancesFormArray.length === 0) { - this.pushInstance(instancesFormArray, 0, instance0); - } - (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); - } - - private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => { - instancesFormArray.push(this.fb.group({ - id: x, - attributeLwm2m: instanceNew.attributeLwm2m, - resources: {value: instanceNew.resources, disabled: this.disabled} - })); - } - private diffBetweenSet(firstSet: Set, secondSet: Set): Set { return new Set([...Array.from(firstSet)].filter(x => !secondSet.has(x))); } - private instancesToSetId = (instances: Instance[]): Set => { + private instancesToSetId(instances: Instance[]): Set { return new Set(instances.map(x => x.id)); } - getNameObjectLwm2m = (objectName: string, idVerObj: string): string => { - return objectName + ' <' + idVerObj + '>'; - } - getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => { - return ' instance <' + idVerObj + '/' + instance.id +'>'; - } - - updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => { - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls - .find(e => e.value.keyId === objectKeyId) as FormGroup; - objectLwM2MFormGroup.patchValue({attributeLwm2m: event}); - } - - updateAttributeLwm2mInstance = (event: Event, indexInstance: number, objectKeyId: number): void => { - - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls - .find(e => e.value.keyId === objectKeyId) as FormGroup; - const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray; - instancesFormArray.at(indexInstance).patchValue({attributeLwm2m: event}); - } - - disableObserveInstance = (instance: AbstractControl): boolean => { - const checkedAttrTelemetry = this.observeInstance(instance); - if (checkedAttrTelemetry) { - instance.get(ATTRIBUTE_LWM2M).patchValue(null); - } - return checkedAttrTelemetry; - } - - disableObserveObject = (index: number): boolean => { - const object = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).at(index) as FormGroup; - const instances = object.controls.instances as FormArray; - const checkedAttrTelemetry = instances.controls.filter(instance => !this.disableObserveInstance(instance)); - if (checkedAttrTelemetry.length === 0) { - object.controls.attributeLwm2m.patchValue(null); - } - return checkedAttrTelemetry.length === 0; - } - - - observeInstance = (instance: AbstractControl): boolean => { - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; - if (isDefinedAndNotNull(resources)) { - const checkedAttribute = resources.filter(resource => resource[ATTRIBUTE]); - const checkedTelemetry = resources.filter(resource => resource[TELEMETRY]); - return checkedAttribute.length === 0 && checkedTelemetry.length === 0; - } - return false; + getNameObject = (objectLwM2M: ObjectLwM2M): string => { + return `${objectLwM2M.name} <${objectLwM2M.id}>`; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts index 4575d2bfff..6be30b3fc8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts @@ -18,7 +18,7 @@ import { NgModule } from '@angular/core'; import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component'; import { Lwm2mObjectListComponent } from './lwm2m-object-list.component'; import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component'; -import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component'; +import { Lwm2mObserveAttrTelemetryResourcesComponent } from './lwm2m-observe-attr-telemetry-resources.component'; import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component'; import { Lwm2mAttributesComponent } from './lwm2m-attributes.component'; import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component'; @@ -27,6 +27,7 @@ import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-insta import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@app/shared/shared.module'; +import { Lwm2mObserveAttrTelemetryInstancesComponent } from './lwm2m-observe-attr-telemetry-instances.component'; @NgModule({ declarations: @@ -34,13 +35,14 @@ import { SharedModule } from '@app/shared/shared.module'; Lwm2mDeviceProfileTransportConfigurationComponent, Lwm2mObjectListComponent, Lwm2mObserveAttrTelemetryComponent, - Lwm2mObserveAttrTelemetryResourceComponent, + Lwm2mObserveAttrTelemetryResourcesComponent, Lwm2mAttributesDialogComponent, Lwm2mAttributesComponent, Lwm2mAttributesKeyListComponent, Lwm2mDeviceConfigServerComponent, Lwm2mObjectAddInstancesDialogComponent, - Lwm2mObjectAddInstancesListComponent + Lwm2mObjectAddInstancesListComponent, + Lwm2mObserveAttrTelemetryInstancesComponent ], imports: [ CommonModule, @@ -50,13 +52,14 @@ import { SharedModule } from '@app/shared/shared.module'; Lwm2mDeviceProfileTransportConfigurationComponent, Lwm2mObjectListComponent, Lwm2mObserveAttrTelemetryComponent, - Lwm2mObserveAttrTelemetryResourceComponent, + Lwm2mObserveAttrTelemetryResourcesComponent, Lwm2mAttributesDialogComponent, Lwm2mAttributesComponent, Lwm2mAttributesKeyListComponent, Lwm2mDeviceConfigServerComponent, Lwm2mObjectAddInstancesDialogComponent, - Lwm2mObjectAddInstancesListComponent + Lwm2mObjectAddInstancesListComponent, + Lwm2mObserveAttrTelemetryInstancesComponent ], providers: [ ] diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 0f419457f0..bc48182e9d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -20,8 +20,6 @@ export const PAGE_SIZE_LIMIT = 50; export const INSTANCES = 'instances'; export const INSTANCE = 'instance'; export const RESOURCES = 'resources'; -export const ATTRIBUTE_LWM2M = 'attributeLwm2m'; -export const CLIENT_LWM2M = 'clientLwM2M'; export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; export const OBSERVE = 'observe'; export const ATTRIBUTE = 'attribute'; @@ -184,7 +182,7 @@ export interface ObservableAttributes { attribute: string[]; telemetry: string[]; keyName: {}; - attributeLwm2m?: AttributesNameValueMap; + attributeLwm2m?: AttributesNameValueMap[]; } export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { @@ -222,7 +220,7 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { attribute: [], telemetry: [], keyName: {}, - attributeLwm2m: {} + attributeLwm2m: [] }; } @@ -237,6 +235,8 @@ export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSetting }; } +export type ResourceSettingTelemetry = 'observe' | 'attribute' | 'telemetry'; + export interface ResourceLwM2M { id: number; name: string; @@ -244,12 +244,12 @@ export interface ResourceLwM2M { attribute: boolean; telemetry: boolean; keyName: string; - attributeLwm2m?: AttributesNameValueMap; + attributes?: AttributesNameValueMap; } export interface Instance { id: number; - attributeLwm2m?: AttributesNameValueMap; + attributes?: AttributesNameValueMap; resources: ResourceLwM2M[]; } @@ -265,7 +265,7 @@ export interface ObjectLwM2M { name: string; multiple?: boolean; mandatory?: boolean; - attributeLwm2m?: AttributesNameValueMap; + attributes?: AttributesNameValueMap; instances?: Instance []; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6cff8b93cf..89c31a70c2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1228,25 +1228,20 @@ "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", "valid-id-instance": "Instance number '{{instance}}' no validated. { count, plural, 1 {Max value='{{max}}'} 2 {Min value='{{min}}'} other {Must be only number} }", "model-tab": "LWM2M Model", - "add-instances-tip": "Add new instances", + "add-new-instances": "Add new instances", "instances-list": "Instances list", "instances-input": "Input Instance Id value", "instances-input-holder": "Input Instance number...", - "instance-label": "Instance", + "instance": "Instance", "resource-label": " Resource name", "observe-label": "Observe", "attribute-label": "Attribute", "telemetry-label": "Telemetry", - "key-name-label": "Key Name", - "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)", - "is-observe-tip": "Is Observe", - "not-observe-tip": "To observe select telemetry or attributes first", - "is-attr-tip": "Is Attribute", - "is-telemetry-tip": "Is Telemetry", - "key-name-tip": "Key Name in Camel format", - "edit-attributes-select": "To edit attributes select telemetry or attributes", + "edit-observe-select": "To edit observe select telemetry or attribute", + "edit-attributes-select": "To edit attributes select telemetry or attribute", "no-attributes-set": "No attributes set", - "key-name": "Key Name", + "key-name": "Key name", + "key-name-required": "Key name is required", "attribute-name": "Name attribute", "attribute-name-required": "Name attribute is required.", "attribute-value": "Attribute value", From 6bc5f7575da47a91d78436dfaa625069f0418910 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 29 Jun 2021 17:26:37 +0300 Subject: [PATCH 148/151] Composite Operations parameter --- .../common/data/device/data/lwm2m/OtherConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java index 81d9d56a8c..c940cd5e85 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -27,6 +27,6 @@ public class OtherConfiguration { private PowerMode powerMode; private String fwUpdateResource; private String swUpdateResource; - private boolean composite; + private boolean compositeOperationsSupport; } From 393d4227847a156f7810607d3d4c490d551b1707 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 29 Jun 2021 20:06:34 +0300 Subject: [PATCH 149/151] fetch all clients after startUp --- .../device/DeviceActorMessageProcessor.java | 4 +- .../lwm2m/server/LwM2mTransportUtil.java | 12 ------ .../DefaultLwM2MAttributesService.java | 3 -- .../lwm2m/server/client/LwM2mClient.java | 2 + .../server/client/LwM2mClientContextImpl.java | 41 +++++++++++++------ .../server/store/TbDummyLwM2MClientStore.java | 8 ++++ .../server/store/TbLwM2MClientStore.java | 4 ++ .../server/store/TbLwM2mSecurityStore.java | 3 -- .../server/store/TbRedisLwM2MClientStore.java | 32 +++++++++++++++ 9 files changed, 77 insertions(+), 32 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 2d5f107d61..4d41b258f3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -269,7 +269,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); if (requestMd != null) { log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); - systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null); + if (requestMd.getMsg().getMsg().isPersisted()) { + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null); + } systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 923c9d7bad..38f3877cb7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -16,34 +16,24 @@ package org.thingsboard.server.transport.lwm2m.server; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mMultipleResource; -import org.eclipse.leshan.core.node.LwM2mNode; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; -import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.request.SimpleDownlinkRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.registration.Registration; -import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; @@ -56,10 +46,8 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMs import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java index 0f0d95610b..3bf6883e7b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java @@ -23,9 +23,6 @@ import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.ota.OtaPackageKey; -import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.gen.transport.TransportProtos; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 8cf6f20f36..41e4b88f28 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.client; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -60,6 +61,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.f import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; @Slf4j +@EqualsAndHashCode(of = {"endpoint"}) public class LwM2mClient implements Serializable { private static final long serialVersionUID = 8793482946289222623L; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 261220f013..65028a599a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -26,9 +26,9 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.transport.TransportDeviceProfileCache; -import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; @@ -68,6 +68,17 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map profiles = new ConcurrentHashMap<>(); + @AfterStartUp + public void init() { + String nodeId = context.getNodeId(); + Set fetchedClients = clientStore.getAll(); + log.debug("Fetched clients from store: {}", fetchedClients); + fetchedClients.forEach(client -> { + lwM2mClientsByEndpoint.put(client.getEndpoint(), client); + updateFetchedClient(nodeId, client); + }); + } + @Override public LwM2mClient getClientByEndpoint(String endpoint) { return lwM2mClientsByEndpoint.computeIfAbsent(endpoint, ep -> { @@ -78,23 +89,27 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { client = new LwM2mClient(nodeId, ep); } else { log.debug("[{}] fetched client from store: {}", endpoint, client); - boolean updated = false; - if (client.getRegistration() != null) { - lwM2mClientsByRegistrationId.put(client.getRegistration().getId(), client); - } - if (client.getSession() != null) { - client.refreshSessionId(nodeId); - sessionManager.register(client.getSession()); - updated = true; - } - if (updated) { - clientStore.put(client); - } + updateFetchedClient(nodeId, client); } return client; }); } + private void updateFetchedClient(String nodeId, LwM2mClient client) { + boolean updated = false; + if (client.getRegistration() != null) { + lwM2mClientsByRegistrationId.put(client.getRegistration().getId(), client); + } + if (client.getSession() != null) { + client.refreshSessionId(nodeId); + sessionManager.register(client.getSession()); + updated = true; + } + if (updated) { + clientStore.put(client); + } + } + @Override public Optional register(LwM2mClient client, Registration registration) throws LwM2MClientStateException { TransportProtos.SessionInfoProto oldSession = null; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbDummyLwM2MClientStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbDummyLwM2MClientStore.java index 3708bf4b0e..767ab43493 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbDummyLwM2MClientStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbDummyLwM2MClientStore.java @@ -17,12 +17,20 @@ package org.thingsboard.server.transport.lwm2m.server.store; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.Collections; +import java.util.Set; + public class TbDummyLwM2MClientStore implements TbLwM2MClientStore { @Override public LwM2mClient get(String endpoint) { return null; } + @Override + public Set getAll() { + return Collections.emptySet(); + } + @Override public void put(LwM2mClient client) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MClientStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MClientStore.java index 16e4e9e90a..55b0423b05 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MClientStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2MClientStore.java @@ -17,10 +17,14 @@ package org.thingsboard.server.transport.lwm2m.server.store; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.Set; + public interface TbLwM2MClientStore { LwM2mClient get(String endpoint); + Set getAll(); + void put(LwM2mClient client); void remove(String endpoint); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java index bf1f275f32..ac0d3d0e68 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mSecurityStore.java @@ -23,12 +23,9 @@ import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInf import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbRedisLwM2MClientStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbRedisLwM2MClientStore.java index a408cc22c4..d735eed26e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbRedisLwM2MClientStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbRedisLwM2MClientStore.java @@ -16,9 +16,17 @@ package org.thingsboard.server.transport.lwm2m.server.store; import org.nustaq.serialization.FSTConfiguration; +import org.springframework.data.redis.connection.RedisClusterConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class TbRedisLwM2MClientStore implements TbLwM2MClientStore { private static final String CLIENT_EP = "CLIENT#EP#"; @@ -42,6 +50,30 @@ public class TbRedisLwM2MClientStore implements TbLwM2MClientStore { } } + @Override + public Set getAll() { + try (var connection = connectionFactory.getConnection()) { + Set clients = new HashSet<>(); + ScanOptions scanOptions = ScanOptions.scanOptions().count(100).match(CLIENT_EP + "*").build(); + List> scans = new ArrayList<>(); + if (connection instanceof RedisClusterConnection) { + ((RedisClusterConnection) connection).clusterGetNodes().forEach(node -> { + scans.add(((RedisClusterConnection) connection).scan(node, scanOptions)); + }); + } else { + scans.add(connection.scan(scanOptions)); + } + + scans.forEach(scan -> { + scan.forEachRemaining(key -> { + byte[] element = connection.get(key); + clients.add((LwM2mClient) serializer.asObject(element)); + }); + }); + return clients; + } + } + @Override public void put(LwM2mClient client) { byte[] clientSerialized = serializer.asByteArray(client); From d8ee95507925f76cc772b3b49b55c54d61766800 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 29 Jun 2021 17:55:40 +0300 Subject: [PATCH 150/151] implemented sendMsgsAfterSleeping --- .../server/client/LwM2mClientContext.java | 2 + .../server/client/LwM2mClientContextImpl.java | 38 ++++++++++++++++++- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 19 ---------- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index 8b8186f3db..a7305177e1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -58,4 +58,6 @@ public interface LwM2mClientContext { void update(LwM2mClient lwM2MClient); void removeCredentials(TransportProtos.SessionInfoProto sessionInfo); + + void sendMsgsAfterSleeping(LwM2mClient lwM2MClient); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 261220f013..3802ef8234 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -21,12 +21,15 @@ import org.eclipse.leshan.core.SecurityMode; import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.transport.TransportDeviceProfileCache; -import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; @@ -34,9 +37,11 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.session.LwM2MSessionManager; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientStore; import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import java.util.Arrays; import java.util.Collection; @@ -64,6 +69,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { private final TbLwM2MClientStore clientStore; private final LwM2MSessionManager sessionManager; private final TransportDeviceProfileCache deviceProfileCache; + + @Autowired + @Lazy + private DefaultLwM2MUplinkMsgHandler defaultLwM2MUplinkMsgHandler; + @Autowired + @Lazy + private LwM2MOtaUpdateService otaUpdateService; + private final Map lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); private final Map profiles = new ConcurrentHashMap<>(); @@ -143,6 +156,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { } client.setRegistration(registration); clientStore.put(client); + sendMsgsAfterSleeping(client); } finally { client.unlock(); } @@ -233,6 +247,28 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { //TODO: implement } + @Override + public void sendMsgsAfterSleeping(LwM2mClient lwM2MClient) { + if (LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) { + PowerMode powerMode = lwM2MClient.getPowerMode(); + if (powerMode == null) { + Lwm2mDeviceProfileTransportConfiguration deviceProfile = getProfile(lwM2MClient.getProfileId()); + powerMode = deviceProfile.getClientLwM2mSettings().getPowerMode(); + } + + if (PowerMode.PSM.equals(powerMode) || PowerMode.E_DRX.equals(powerMode)) { + defaultLwM2MUplinkMsgHandler.initAttributes(lwM2MClient); + TransportProtos.TransportToDeviceActorMsg persistentRpcRequestMsg = TransportProtos.TransportToDeviceActorMsg + .newBuilder() + .setSessionInfo(lwM2MClient.getSession()) + .setSendPendingRPC(TransportProtos.SendPendingRPCMsg.newBuilder().build()) + .build(); + context.getTransportService().process(persistentRpcRequestMsg, TransportServiceCallback.EMPTY); + otaUpdateService.init(lwM2MClient); + } + } + } + @Override public Collection getLwM2mClients() { return lwM2mClientsByEndpoint.values(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index cd8b19c60a..9bdf53741d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -42,7 +42,6 @@ import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; @@ -390,24 +389,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client is awake!"); - - if (LwM2MClientState.REGISTERED.equals(lwM2MClient.getState())) { - PowerMode powerMode = lwM2MClient.getPowerMode(); - if (powerMode == null) { - Lwm2mDeviceProfileTransportConfiguration deviceProfile = clientContext.getProfile(lwM2MClient.getProfileId()); - powerMode = deviceProfile.getClientLwM2mSettings().getPowerMode(); - } - - if (PowerMode.PSM.equals(powerMode) || PowerMode.E_DRX.equals(powerMode)) { - initAttributes(lwM2MClient); - TransportProtos.TransportToDeviceActorMsg persistentRpcRequestMsg = TransportProtos.TransportToDeviceActorMsg - .newBuilder() - .setSessionInfo(lwM2MClient.getSession()) - .setSendPendingRPC(TransportProtos.SendPendingRPCMsg.newBuilder().build()) - .build(); - transportService.process(persistentRpcRequestMsg, TransportServiceCallback.EMPTY); - } - } } /** From 290074ffcdd10bcc534f3ebcd278e0c7bc8dbba8 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 30 Jun 2021 11:53:00 +0300 Subject: [PATCH 151/151] replay downlink on device awake --- .../lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 9bdf53741d..1850212b6e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -387,8 +387,9 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @Override public void onAwakeDev(Registration registration) { log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); - LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); - logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client is awake!"); + LwM2mClient client = this.clientContext.getClientByEndpoint(registration.getEndpoint()); + logService.log(client, LOG_LWM2M_INFO + ": Client is awake!"); + clientContext.sendMsgsAfterSleeping(client); } /**