diff --git a/application/pom.xml b/application/pom.xml index 8449246059..e0b6b98d9a 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,10 +20,9 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT thingsboard - org.thingsboard application jar @@ -50,12 +49,12 @@ linux-x86_64 - org.thingsboard - extensions-api + org.thingsboard.rule-engine + rule-engine-api - org.thingsboard - extensions-core + org.thingsboard.rule-engine + rule-engine-components org.thingsboard.common @@ -257,6 +256,10 @@ org.hsqldb hsqldb + + org.javadelight + delight-nashorn-sandbox + @@ -463,48 +466,6 @@ org.apache.maven.plugins maven-dependency-plugin - - copy-extensions - package - - copy - - - ${project.build.directory}/extensions - - - org.thingsboard.extensions - extension-rabbitmq - extension - - - org.thingsboard.extensions - extension-rest-api-call - extension - - - org.thingsboard.extensions - extension-kafka - extension - - - org.thingsboard.extensions - extension-mqtt - extension - - - org.thingsboard.extensions - extension-sqs - extension - - - org.thingsboard.extensions - extension-sns - extension - - - - copy-winsw-service package diff --git a/application/src/main/conf/thingsboard.conf b/application/src/main/conf/thingsboard.conf index a6e404d5ad..2baee7de41 100644 --- a/application/src/main/conf/thingsboard.conf +++ b/application/src/main/conf/thingsboard.conf @@ -14,7 +14,7 @@ # limitations under the License. # -export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@" +export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@" export LOG_FILENAME=${pkg.name}.out export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql diff --git a/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json b/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json deleted file mode 100644 index 6181f4920c..0000000000 --- a/application/src/main/data/json/demo/plugins/demo_device_messaging_rpc_plugin.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "apiToken": "messaging", - "name": "Demo Device Messaging RPC Plugin", - "clazz": "org.thingsboard.server.extensions.core.plugin.messaging.DeviceMessagingPlugin", - "publicAccess": false, - "state": "ACTIVE", - "configuration": { - "maxDeviceCountPerCustomer": 1024, - "defaultTimeout": 20000, - "maxTimeout": 60000 - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/plugins/demo_email_plugin.json b/application/src/main/data/json/demo/plugins/demo_email_plugin.json deleted file mode 100644 index 40225f8c8c..0000000000 --- a/application/src/main/data/json/demo/plugins/demo_email_plugin.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "apiToken": "mail", - "name": "Demo Email Plugin", - "clazz": "org.thingsboard.server.extensions.core.plugin.mail.MailPlugin", - "publicAccess": true, - "state": "ACTIVE", - "configuration": { - "host": "smtp.sendgrid.net", - "port": 2525, - "username": "apikey", - "password": "your_api_key", - "otherProperties": [ - { - "key": "mail.smtp.auth", - "value": "true" - }, - { - "key": "mail.smtp.timeout", - "value": "10000" - }, - { - "key": "mail.smtp.starttls.enable", - "value": "true" - } - ] - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json b/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json deleted file mode 100644 index d5ac2d4469..0000000000 --- a/application/src/main/data/json/demo/plugins/demo_time_rpc_plugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "apiToken": "time", - "name": "Demo Time RPC Plugin", - "clazz": "org.thingsboard.server.extensions.core.plugin.time.TimePlugin", - "publicAccess": false, - "state": "ACTIVE", - "configuration": { - "timeFormat": "yyyy MM dd HH:mm:ss.SSS" - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/rules/demo_alarm_rule.json b/application/src/main/data/json/demo/rules/demo_alarm_rule.json deleted file mode 100644 index 12565d8c16..0000000000 --- a/application/src/main/data/json/demo/rules/demo_alarm_rule.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "Demo Alarm Rule", - "state": "ACTIVE", - "weight": 0, - "pluginToken": "mail", - "filters": [ - { - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter", - "name": "MsgTypeFilter", - "configuration": { - "messageTypes": [ - "POST_TELEMETRY", - "POST_ATTRIBUTES", - "GET_ATTRIBUTES" - ] - } - }, - { - "clazz": "org.thingsboard.server.extensions.core.filter.DeviceTelemetryFilter", - "name": "Temperature filter", - "configuration": { - "filter": "typeof temperature !== 'undefined' && temperature >= 100" - } - } - ], - "processor": { - "clazz": "org.thingsboard.server.extensions.core.processor.AlarmDeduplicationProcessor", - "name": "AlarmDeduplicationProcessor", - "configuration": { - "alarmIdTemplate": "[$date.get('yyyy-MM-dd HH:mm')] Device $cs.get('serialNumber')($cs.get('model')) temperature is high!", - "alarmBodyTemplate": "[$date.get('yyyy-MM-dd HH:mm:ss')] Device $cs.get('serialNumber')($cs.get('model')) temperature is $temp.valueAsString!" - } - }, - "action": { - "clazz": "org.thingsboard.server.extensions.core.action.mail.SendMailAction", - "name": "Send Mail Action", - "configuration": { - "sendFlag": "isNewAlarm", - "fromTemplate": "thingsboard@gmail.com", - "toTemplate": "thingsboard@gmail.com", - "subjectTemplate": "$alarmId", - "bodyTemplate": "$alarmBody" - } - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json b/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json deleted file mode 100644 index 391c121ca2..0000000000 --- a/application/src/main/data/json/demo/rules/demo_gettime_rpc_rule.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Demo getTime RPC Rule", - "state": "ACTIVE", - "weight": 0, - "pluginToken": "time", - "filters": [ - { - "configuration": { - "messageTypes": [ - "RPC_REQUEST" - ] - }, - "name": "RPC Request Filter", - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter" - }, - { - "configuration": { - "methodNames": [ - { - "name": "getTime" - } - ] - }, - "name": "getTime method filter", - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter" - } - ], - "processor": null, - "action": { - "configuration": {}, - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction", - "name": "getTimeAction" - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json b/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json deleted file mode 100644 index 50021e4bde..0000000000 --- a/application/src/main/data/json/demo/rules/demo_messaging_rpc_rule.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "Demo Messaging RPC Rule", - "state": "ACTIVE", - "weight": 0, - "pluginToken": "messaging", - "filters": [ - { - "configuration": { - "messageTypes": [ - "RPC_REQUEST" - ] - }, - "name": "RPC Request Filter", - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter" - }, - { - "configuration": { - "methodNames": [ - { - "name": "getDevices" - }, - { - "name": "sendMsg" - } - ] - }, - "name": "Messaging methods filter", - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter" - } - ], - "processor": null, - "action": { - "configuration": {}, - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction", - "name": "Messaging RPC Action" - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/system/plugins/system_rpc_plugin.json b/application/src/main/data/json/system/plugins/system_rpc_plugin.json deleted file mode 100644 index 7245faaa5e..0000000000 --- a/application/src/main/data/json/system/plugins/system_rpc_plugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "apiToken": "rpc", - "name": "System RPC Plugin", - "clazz": "org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin", - "publicAccess": true, - "state": "ACTIVE", - "configuration": { - "defaultTimeout": 20000 - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/system/plugins/system_telemetry_plugin.json b/application/src/main/data/json/system/plugins/system_telemetry_plugin.json deleted file mode 100644 index 93a40e0ab3..0000000000 --- a/application/src/main/data/json/system/plugins/system_telemetry_plugin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "apiToken": "telemetry", - "name": "System Telemetry Plugin", - "clazz": "org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin", - "publicAccess": true, - "state": "ACTIVE", - "configuration": {}, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/system/rules/system_telemetry_rule.json b/application/src/main/data/json/system/rules/system_telemetry_rule.json deleted file mode 100644 index c8166bf710..0000000000 --- a/application/src/main/data/json/system/rules/system_telemetry_rule.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "System Telemetry Rule", - "state": "ACTIVE", - "weight": 0, - "pluginToken": "telemetry", - "filters": [ - { - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter", - "name": "TelemetryFilter", - "configuration": { - "messageTypes": [ - "POST_TELEMETRY", - "POST_ATTRIBUTES", - "GET_ATTRIBUTES" - ] - } - } - ], - "processor": null, - "action": { - "clazz": "org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction", - "name": "TelemetryMsgConverterAction", - "configuration": { - "timeUnit": "DAYS", - "ttlValue": 365 - } - }, - "additionalInfo": null -} \ No newline at end of file diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json new file mode 100644 index 0000000000..225ccdc1f0 --- /dev/null +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -0,0 +1,98 @@ +{ + "ruleChain": { + "additionalInfo": null, + "name": "Root Rule Chain", + "firstRuleNodeId": null, + "root": true, + "debugMode": false, + "configuration": null + }, + "metadata": { + "firstNodeIndex": 2, + "nodes": [ + { + "additionalInfo": { + "layoutX": 824, + "layoutY": 156 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "SaveTS", + "debugMode": false, + "configuration": { + "defaultTTL": 0 + } + }, + { + "additionalInfo": { + "layoutX": 825, + "layoutY": 52 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", + "name": "save client attributes", + "debugMode": false, + "configuration": { + "scope": "CLIENT_SCOPE" + } + }, + { + "additionalInfo": { + "layoutX": 347, + "layoutY": 149 + }, + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", + "name": "Message Type Switch", + "debugMode": false, + "configuration": { + "version": 0 + } + }, + { + "additionalInfo": { + "layoutX": 825, + "layoutY": 266 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log RPC", + "debugMode": false, + "configuration": { + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + } + }, + { + "additionalInfo": { + "layoutX": 825, + "layoutY": 379 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log Other", + "debugMode": false, + "configuration": { + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + } + } + ], + "connections": [ + { + "fromIndex": 2, + "toIndex": 4, + "type": "Other" + }, + { + "fromIndex": 2, + "toIndex": 1, + "type": "Post attributes" + }, + { + "fromIndex": 2, + "toIndex": 0, + "type": "Post telemetry" + }, + { + "fromIndex": 2, + "toIndex": 3, + "type": "RPC Request" + } + ], + "ruleChainConnections": null + } +} \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.cql b/application/src/main/data/upgrade/2.0.0/schema_update.cql new file mode 100644 index 0000000000..5f46878d6a --- /dev/null +++ b/application/src/main/data/upgrade/2.0.0/schema_update.cql @@ -0,0 +1,103 @@ +-- +-- Copyright © 2016-2018 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. +-- + +CREATE TABLE IF NOT EXISTS thingsboard.msg_queue ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + ts bigint, + msg blob, + PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts)) +WITH CLUSTERING ORDER BY (ts DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + msg_id timeuuid, + PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id)) +WITH CLUSTERING ORDER BY (msg_id DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + PRIMARY KEY ((node_id, cluster_partition), ts_partition)) +WITH CLUSTERING ORDER BY (ts_partition DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( + id uuid, + tenant_id uuid, + name text, + search_text text, + first_rule_node_id uuid, + root boolean, + debug_mode boolean, + configuration text, + additional_info text, + PRIMARY KEY (id, tenant_id) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS + SELECT * + from thingsboard.rule_chain + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id ) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); + +CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( + id uuid, + rule_chain_id uuid, + type text, + name text, + debug_mode boolean, + search_text text, + configuration text, + additional_info text, + PRIMARY KEY (id) +); + +DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_plugin_token; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_tenant_and_search_text; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_api_token; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_tenant_and_search_text; + +DROP TABLE IF EXISTS thingsboard.rule; +DROP TABLE IF EXISTS thingsboard.plugin; diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.sql b/application/src/main/data/upgrade/2.0.0/schema_update.sql new file mode 100644 index 0000000000..f16e334041 --- /dev/null +++ b/application/src/main/data/upgrade/2.0.0/schema_update.sql @@ -0,0 +1,44 @@ +-- +-- Copyright © 2016-2018 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. +-- + +CREATE TABLE IF NOT EXISTS rule_chain ( + id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, + additional_info varchar, + configuration varchar(10000000), + name varchar(255), + first_rule_node_id varchar(31), + root boolean, + debug_mode boolean, + search_text varchar(255), + tenant_id varchar(31) +); + +CREATE TABLE IF NOT EXISTS rule_node ( + id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, + rule_chain_id varchar(31), + additional_info varchar, + configuration varchar(10000000), + type varchar(255), + name varchar(255), + debug_mode boolean, + search_text varchar(255) +); + +DROP TABLE rule; +DROP TABLE plugin; + +DELETE FROM alarm WHERE originator_type = 3 OR originator_type = 4; +UPDATE alarm SET originator_type = (originator_type - 2) where originator_type > 2; 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 77953c9dc1..d33734e14c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -21,22 +21,27 @@ import akka.actor.Scheduler; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; @@ -44,118 +49,222 @@ import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.plugin.PluginService; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; import org.thingsboard.server.service.component.ComponentDiscoveryService; - +import org.thingsboard.server.service.encoding.DataDecodingEncodingService; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.executors.ExternalCallExecutorService; +import org.thingsboard.server.service.mail.MailExecutorService; +import org.thingsboard.server.service.queue.MsgQueueService; +import org.thingsboard.server.service.rpc.DeviceRpcService; +import org.thingsboard.server.service.script.JsExecutorService; +import org.thingsboard.server.service.script.JsSandboxService; +import org.thingsboard.server.service.state.DeviceStateService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import javax.annotation.Nullable; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Optional; +@Slf4j @Component public class ActorSystemContext { private static final String AKKA_CONF_FILE_NAME = "actor-system.conf"; protected final ObjectMapper mapper = new ObjectMapper(); - @Getter @Setter private ActorService actorService; + @Getter + @Setter + private ActorService actorService; + + @Autowired + @Getter + private DiscoveryService discoveryService; + + @Autowired + @Getter + @Setter + private ComponentDiscoveryService componentService; + + @Autowired + @Getter + private ClusterRoutingService routingService; + + @Autowired + @Getter + private ClusterRpcService rpcService; + + @Autowired + @Getter + private DataDecodingEncodingService encodingService; + + @Autowired + @Getter + private DeviceAuthService deviceAuthService; + + @Autowired + @Getter + private DeviceService deviceService; + + @Autowired + @Getter + private AssetService assetService; + + @Autowired + @Getter + private TenantService tenantService; + + @Autowired + @Getter + private CustomerService customerService; @Autowired - @Getter private DiscoveryService discoveryService; + @Getter + private UserService userService; @Autowired - @Getter @Setter private ComponentDiscoveryService componentService; + @Getter + private RuleChainService ruleChainService; @Autowired - @Getter private ClusterRoutingService routingService; + @Getter + private TimeseriesService tsService; @Autowired - @Getter private ClusterRpcService rpcService; + @Getter + private AttributesService attributesService; @Autowired - @Getter private DeviceAuthService deviceAuthService; + @Getter + private EventService eventService; @Autowired - @Getter private DeviceService deviceService; + @Getter + private AlarmService alarmService; @Autowired - @Getter private AssetService assetService; + @Getter + private RelationService relationService; @Autowired - @Getter private TenantService tenantService; + @Getter + private AuditLogService auditLogService; @Autowired - @Getter private CustomerService customerService; + @Getter + private TelemetrySubscriptionService tsSubService; @Autowired - @Getter private RuleService ruleService; + @Getter + private DeviceRpcService deviceRpcService; @Autowired - @Getter private PluginService pluginService; + @Getter + private JsSandboxService jsSandbox; @Autowired - @Getter private TimeseriesService tsService; + @Getter + private JsExecutorService jsExecutor; @Autowired - @Getter private AttributesService attributesService; + @Getter + private MailExecutorService mailExecutor; @Autowired - @Getter private EventService eventService; + @Getter + private DbCallbackExecutorService dbCallbackExecutor; @Autowired - @Getter private AlarmService alarmService; + @Getter + private ExternalCallExecutorService externalCallExecutorService; @Autowired - @Getter private RelationService relationService; + @Getter + private MailService mailService; @Autowired - @Getter private AuditLogService auditLogService; + @Getter + private MsgQueueService msgQueueService; @Autowired - @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; + @Getter + private DeviceStateService deviceStateService; + + @Value("${cluster.partition_id}") + @Getter + private long queuePartitionId; + + @Value("${actors.session.max_concurrent_sessions_per_device:1}") + @Getter + private long maxConcurrentSessionsPerDevice; @Value("${actors.session.sync.timeout}") - @Getter private long syncSessionTimeout; + @Getter + private long syncSessionTimeout; - @Value("${actors.plugin.termination.delay}") - @Getter private long pluginActorTerminationDelay; + @Value("${actors.queue.enabled}") + @Getter + private boolean queuePersistenceEnabled; - @Value("${actors.plugin.processing.timeout}") - @Getter private long pluginProcessingTimeout; + @Value("${actors.queue.timeout}") + @Getter + private long queuePersistenceTimeout; - @Value("${actors.plugin.error_persist_frequency}") - @Getter private long pluginErrorPersistFrequency; + @Value("${actors.client_side_rpc.timeout}") + @Getter + private long clientSideRpcTimeout; - @Value("${actors.rule.termination.delay}") - @Getter private long ruleActorTerminationDelay; + @Value("${actors.rule.chain.error_persist_frequency}") + @Getter + private long ruleChainErrorPersistFrequency; - @Value("${actors.rule.error_persist_frequency}") - @Getter private long ruleErrorPersistFrequency; + @Value("${actors.rule.node.error_persist_frequency}") + @Getter + private long ruleNodeErrorPersistFrequency; @Value("${actors.statistics.enabled}") - @Getter private boolean statisticsEnabled; + @Getter + private boolean statisticsEnabled; @Value("${actors.statistics.persist_frequency}") - @Getter private long statisticsPersistFrequency; + @Getter + private long statisticsPersistFrequency; @Value("${actors.tenant.create_components_on_init}") - @Getter private boolean tenantComponentsInitEnabled; + @Getter + private boolean tenantComponentsInitEnabled; - @Getter @Setter private ActorSystem actorSystem; + @Value("${actors.rule.allow_system_mail_service}") + @Getter + private boolean allowSystemMailService; - @Getter @Setter private ActorRef appActor; + @Getter + @Setter + private ActorSystem actorSystem; - @Getter @Setter private ActorRef sessionManagerActor; + @Getter + @Setter + private ActorRef appActor; - @Getter @Setter private ActorRef statsActor; + @Getter + @Setter + private ActorRef sessionManagerActor; - @Getter private final Config config; + @Getter + @Setter + private ActorRef statsActor; + + @Getter + private final Config config; public ActorSystemContext() { config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); @@ -187,7 +296,7 @@ public class ActorSystemContext { eventService.save(event); } - private String toString(Exception e) { + private String toString(Throwable e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); return sw.toString(); @@ -207,4 +316,72 @@ public class ActorSystemContext { private JsonNode toBodyJson(ServerAddress server, String method, String body) { return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body); } + + public String getServerAddress() { + return discoveryService.getCurrentServer().getServerAddress().toString(); + } + + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) { + persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, null); + } + + public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) { + persistDebugAsync(tenantId, entityId, "IN", tbMsg, relationType, error); + } + + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType, Throwable error) { + persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, error); + } + + public void persistDebugOutput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) { + persistDebugAsync(tenantId, entityId, "OUT", tbMsg, relationType, null); + } + + private void persistDebugAsync(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, String relationType, Throwable error) { + try { + Event event = new Event(); + event.setTenantId(tenantId); + event.setEntityId(entityId); + event.setType(DataConstants.DEBUG_RULE_NODE); + + String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData()); + + ObjectNode node = mapper.createObjectNode() + .put("type", type) + .put("server", getServerAddress()) + .put("entityId", tbMsg.getOriginator().getId().toString()) + .put("entityName", tbMsg.getOriginator().getEntityType().name()) + .put("msgId", tbMsg.getId().toString()) + .put("msgType", tbMsg.getType()) + .put("dataType", tbMsg.getDataType().name()) + .put("relationType", relationType) + .put("data", tbMsg.getData()) + .put("metadata", metadata); + + if (error != null) { + node = node.put("error", toString(error)); + } + + event.setBody(node); + ListenableFuture future = eventService.saveAsync(event); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(@Nullable Event event) { + + } + + @Override + public void onFailure(Throwable th) { + log.error("Could not save debug Event for Node", th); + } + }); + } catch (IOException ex) { + log.warn("Failed to persist rule node debug message", ex); + } + } + + public static Exception toException(Throwable error) { + return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); + } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index b475277630..d453e594ba 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -15,55 +15,51 @@ */ package org.thingsboard.server.actors.app; -import akka.actor.*; +import akka.actor.ActorRef; +import akka.actor.LocalActorRef; +import akka.actor.OneForOneStrategy; +import akka.actor.Props; +import akka.actor.SupervisorStrategy; import akka.actor.SupervisorStrategy.Directive; +import akka.actor.Terminated; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.japi.Function; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.plugin.PluginTerminationMsg; -import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.actors.shared.plugin.PluginManager; -import org.thingsboard.server.actors.shared.plugin.SystemPluginManager; -import org.thingsboard.server.actors.shared.rule.RuleManager; -import org.thingsboard.server.actors.shared.rule.SystemRuleManager; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; +import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; import org.thingsboard.server.actors.tenant.TenantActor; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.aware.TenantAwareMsg; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg; import scala.concurrent.duration.Duration; import java.util.HashMap; import java.util.Map; import java.util.Optional; -public class AppActor extends ContextAwareActor { +public class AppActor extends RuleChainManagerActor { private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); - private final RuleManager ruleManager; - private final PluginManager pluginManager; private final TenantService tenantService; private final Map tenantActors; private AppActor(ActorSystemContext systemContext) { - super(systemContext); - this.ruleManager = new SystemRuleManager(systemContext); - this.pluginManager = new SystemPluginManager(systemContext); + super(systemContext, new SystemRuleChainManager(systemContext)); this.tenantService = systemContext.getTenantService(); this.tenantActors = new HashMap<>(); } @@ -77,8 +73,7 @@ public class AppActor extends ContextAwareActor { public void preStart() { logger.info("Starting main system actor."); try { - ruleManager.init(this.context()); - pluginManager.init(this.context()); + initRuleChains(); if (systemContext.isTenantComponentsInitEnabled()) { PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); @@ -96,93 +91,89 @@ public class AppActor extends ContextAwareActor { } @Override - public void onReceive(Object msg) throws Exception { - logger.debug("Received message: {}", msg); - if (msg instanceof ToDeviceActorMsg) { - processDeviceMsg((ToDeviceActorMsg) msg); - } else if (msg instanceof ToPluginActorMsg) { - onToPluginMsg((ToPluginActorMsg) msg); - } else if (msg instanceof ToRuleActorMsg) { - onToRuleMsg((ToRuleActorMsg) msg); - } else if (msg instanceof ToDeviceActorNotificationMsg) { - onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); - } else if (msg instanceof Terminated) { - processTermination((Terminated) msg); - } else if (msg instanceof ClusterEventMsg) { - broadcast(msg); - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof PluginTerminationMsg) { - onPluginTerminated((PluginTerminationMsg) msg); - } else { - logger.warning("Unknown message: {}!", msg); + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case SEND_TO_CLUSTER_MSG: + onPossibleClusterMsg((SendToClusterMsg) msg); + break; + case CLUSTER_EVENT_MSG: + broadcast(msg); + break; + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: + onToDeviceActorMsg((TenantAwareMsg) msg); + break; + case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: + onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg); + default: + return false; } + return true; } - private void onPluginTerminated(PluginTerminationMsg msg) { - pluginManager.remove(msg.getId()); + private void onToDeviceSessionMsg(BasicActorSystemToDeviceSessionActorMsg msg) { + systemContext.getSessionManagerActor().tell(msg, self()); } - private void broadcast(Object msg) { - pluginManager.broadcast(msg); - tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); + private void onPossibleClusterMsg(SendToClusterMsg msg) { + Optional address = systemContext.getRoutingService().resolveById(msg.getEntityId()); + if (address.isPresent()) { + systemContext.getRpcService().tell( + systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg())); + } else { + self().tell(msg.getMsg(), ActorRef.noSender()); + } } - private void onToRuleMsg(ToRuleActorMsg msg) { - ActorRef target; + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { if (SYSTEM_TENANT.equals(msg.getTenantId())) { - target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId()); + //TODO: ashvayka handle this. } else { - target = getOrCreateTenantActor(msg.getTenantId()); + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self()); } - target.tell(msg, ActorRef.noSender()); } - private void onToPluginMsg(ToPluginActorMsg msg) { - ActorRef target; - if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) { - target = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId()); - } else { - target = getOrCreateTenantActor(msg.getPluginTenantId()); - } - target.tell(msg, ActorRef.noSender()); + @Override + protected void broadcast(Object msg) { + super.broadcast(msg); + tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - ActorRef target = null; + ActorRef target; if (SYSTEM_TENANT.equals(msg.getTenantId())) { - Optional pluginId = msg.getPluginId(); - Optional ruleId = msg.getRuleId(); - if (pluginId.isPresent()) { - target = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get()); - } else if (ruleId.isPresent()) { - Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent()); - if (ref.isPresent()) { - target = ref.get(); - } else { - logger.debug("Failed to find actor for rule: [{}]", ruleId); - return; - } - } + target = getEntityActorRef(msg.getEntityId()); } else { target = getOrCreateTenantActor(msg.getTenantId()); } if (target != null) { target.tell(msg, ActorRef.noSender()); + } else { + logger.debug("Invalid component lifecycle msg: {}", msg); } } - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { + private void onToDeviceActorMsg(TenantAwareMsg msg) { getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); } - private void processDeviceMsg(ToDeviceActorMsg toDeviceActorMsg) { - TenantId tenantId = toDeviceActorMsg.getTenantId(); + private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) { + TenantId tenantId = deviceToDeviceActorMsg.getTenantId(); ActorRef tenantActor = getOrCreateTenantActor(tenantId); - if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) { - tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); + if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) { +// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); } else { - tenantActor.tell(toDeviceActorMsg, context().self()); + tenantActor.tell(deviceToDeviceActorMsg, context().self()); } } 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 861c405e08..99d004574e 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 @@ -17,61 +17,73 @@ package org.thingsboard.server.actors.device; import akka.event.Logging; import akka.event.LoggingAdapter; +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.RulesProcessedMsg; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; -import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; -import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.*; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; public class DeviceActor extends ContextAwareActor { private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); - private final TenantId tenantId; - private final DeviceId deviceId; private final DeviceActorMessageProcessor processor; private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { super(systemContext); - this.tenantId = tenantId; - this.deviceId = deviceId; - this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId); + this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId); } @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof RuleChainDeviceMsg) { - processor.process(context(), (RuleChainDeviceMsg) msg); - } else if (msg instanceof RulesProcessedMsg) { - processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg); - } else if (msg instanceof ToDeviceActorMsg) { - processor.process(context(), (ToDeviceActorMsg) msg); - } else if (msg instanceof ToDeviceActorNotificationMsg) { - if (msg instanceof DeviceAttributesEventNotificationMsg) { + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case CLUSTER_EVENT_MSG: + processor.processClusterEventMsg((ClusterEventMsg) msg); + break; + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: + processor.process(context(), (DeviceToDeviceActorMsg) msg); + break; + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); - } else if (msg instanceof ToDeviceRpcRequestPluginMsg) { - processor.processRpcRequest(context(), (ToDeviceRpcRequestPluginMsg) msg); - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg){ + break; + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processCredentialsUpdate(); - } else if (msg instanceof DeviceNameOrTypeUpdateMsg){ + break; + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); - } - } else if (msg instanceof TimeoutMsg) { - processor.processTimeout(context(), (TimeoutMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - processor.processClusterEventMsg((ClusterEventMsg) msg); - } else { - logger.debug("[{}][{}] Unknown msg type.", tenantId, deviceId, msg.getClass().getName()); + break; + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: + processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg); + break; + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: + processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg); + break; + case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: + processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg); + break; + case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG: + processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg); + break; + case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG: + processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg); + break; + case RULE_ENGINE_QUEUE_PUT_ACK_MSG: + processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg); + break; + default: + return false; } + return true; } public static class ActorCreator extends ContextBasedCreator { 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 21112bf4df..85b32857f1 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 @@ -18,37 +18,75 @@ package org.thingsboard.server.actors.device; import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.event.LoggingAdapter; +import com.datastax.driver.core.utils.UUIDs; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.thingsboard.rule.engine.api.RpcError; +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.*; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; -import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.SessionId; +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.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.core.*; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; +import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg; +import org.thingsboard.server.common.msg.core.AttributesUpdateNotification; +import org.thingsboard.server.common.msg.core.AttributesUpdateRequest; +import org.thingsboard.server.common.msg.core.BasicActorSystemToDeviceSessionActorMsg; +import org.thingsboard.server.common.msg.core.BasicCommandAckResponse; +import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse; +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; +import org.thingsboard.server.common.msg.core.GetAttributesRequest; +import org.thingsboard.server.common.msg.core.RuleEngineError; +import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; +import org.thingsboard.server.common.msg.core.SessionCloseMsg; +import org.thingsboard.server.common.msg.core.SessionCloseNotification; +import org.thingsboard.server.common.msg.core.SessionOpenMsg; +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg; +import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg; +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.device.*; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.RpcError; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutIntMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; - -import java.util.*; -import java.util.concurrent.ExecutionException; +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; +import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Predicate; @@ -59,46 +97,46 @@ import java.util.stream.Collectors; */ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { + private final TenantId tenantId; private final DeviceId deviceId; private final Map sessions; private final Map attributeSubscriptions; private final Map rpcSubscriptions; + private final Map toDeviceRpcPendingMap; + private final Map toServerRpcPendingMap; + private final Map pendingMsgs; - private final Map rpcPendingMap; + private final Gson gson = new Gson(); + private final JsonParser jsonParser = new JsonParser(); private int rpcSeq = 0; private String deviceName; private String deviceType; - private DeviceAttributes deviceAttributes; + private TbMsgMetaData defaultMetaData; - public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) { + public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) { super(systemContext, logger); + this.tenantId = tenantId; this.deviceId = deviceId; - this.sessions = new HashMap<>(); + this.sessions = new LinkedHashMap<>(); this.attributeSubscriptions = new HashMap<>(); this.rpcSubscriptions = new HashMap<>(); - this.rpcPendingMap = new HashMap<>(); + this.toDeviceRpcPendingMap = new HashMap<>(); + this.toServerRpcPendingMap = new HashMap<>(); + this.pendingMsgs = new HashMap<>(); initAttributes(); } private void initAttributes() { - //TODO: add invalidation of deviceType cache. Device device = systemContext.getDeviceService().findDeviceById(deviceId); this.deviceName = device.getName(); this.deviceType = device.getType(); - this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE), - fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE)); + this.defaultMetaData = new TbMsgMetaData(); + this.defaultMetaData.putValue("deviceName", deviceName); + this.defaultMetaData.putValue("deviceType", deviceType); } - private void refreshAttributes(DeviceAttributesEventNotificationMsg msg) { - if (msg.isDeleted()) { - msg.getDeletedKeys().forEach(key -> deviceAttributes.remove(key)); - } else { - deviceAttributes.update(msg.getScope(), msg.getValues()); - } - } - - void processRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg) { + void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { ToDeviceRpcRequest request = msg.getMsg(); ToDeviceRpcRequestBody body = request.getBody(); ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( @@ -116,7 +154,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso boolean sent = rpcSubscriptions.size() > 0; Set syncSessionSet = new HashSet<>(); rpcSubscriptions.entrySet().forEach(sub -> { - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sub.getKey()); + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sub.getKey()); sendMsgToSessionActor(response, sub.getValue().getServer()); if (SessionType.SYNC == sub.getValue().getType()) { syncSessionSet.add(sub.getKey()); @@ -125,9 +163,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso syncSessionSet.forEach(rpcSubscriptions::remove); if (request.isOneway() && sent) { - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null); - context.parent().tell(responsePluginMsg, ActorRef.noSender()); logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(msg.getMsg().getId(), msg.getServerAddress(), null, null)); } else { registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); } @@ -139,24 +176,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { - rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); - TimeoutIntMsg timeoutMsg = new TimeoutIntMsg(rpcRequest.getRequestId(), timeout); + private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { + toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); + DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout); scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); } - public void processTimeout(ActorContext context, TimeoutMsg msg) { - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId()); + void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) { + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); if (requestMd != null) { logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION); - context.parent().tell(responsePluginMsg, ActorRef.noSender()); + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), + requestMd.getMsg().getServerAddress(), null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); + } + } + + void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) { + PendingSessionMsgData data = pendingMsgs.remove(msg.getId()); + if (data != null) { + logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId()); + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); + } + } + + void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) { + PendingSessionMsgData data = pendingMsgs.remove(msg.getId()); + if (data != null && data.isReplyOnQueueAck()) { + int remainingAcks = data.getAckMsgCount() - 1; + data.setAckMsgCount(remainingAcks); + logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks); + if (remainingAcks == 0) { + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId()); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress()); + } } } private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional server) { - if (!rpcPendingMap.isEmpty()) { - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId); + if (!toDeviceRpcPendingMap.isEmpty()) { + logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); if (type == SessionType.SYNC) { logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); rpcSubscriptions.remove(sessionId); @@ -166,41 +225,196 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } Set sentOneWayIds = new HashSet<>(); if (type == SessionType.ASYNC) { - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); } else { - rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); } - sentOneWayIds.forEach(rpcPendingMap::remove); + sentOneWayIds.forEach(toDeviceRpcPendingMap::remove); } private Consumer> processPendingRpc(ActorContext context, SessionId sessionId, Optional server, Set sentOneWayIds) { return entry -> { + ToDeviceRpcRequestActorMsg requestActorMsg = entry.getValue().getMsg(); ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg(); ToDeviceRpcRequestBody body = request.getBody(); if (request.isOneway()) { sentOneWayIds.add(entry.getKey()); - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null); - context.parent().tell(responsePluginMsg, ActorRef.noSender()); + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(request.getId(), requestActorMsg.getServerAddress(), null, null)); } ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( entry.getKey(), body.getMethod(), body.getParams() ); - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(rpcRequest, sessionId); + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(rpcRequest, sessionId); sendMsgToSessionActor(response, server); }; } - void process(ActorContext context, ToDeviceActorMsg msg) { + void process(ActorContext context, DeviceToDeviceActorMsg msg) { processSubscriptionCommands(context, msg); processRpcResponses(context, msg); processSessionStateMsgs(msg); + + SessionMsgType sessionMsgType = msg.getPayload().getMsgType(); + if (sessionMsgType.requiresRulesProcessing()) { + switch (sessionMsgType) { + case GET_ATTRIBUTES_REQUEST: + handleGetAttributesRequest(msg); + break; + case POST_ATTRIBUTES_REQUEST: + handlePostAttributesRequest(context, msg); + reportActivity(); + break; + case POST_TELEMETRY_REQUEST: + handlePostTelemetryRequest(context, msg); + reportActivity(); + break; + case TO_SERVER_RPC_REQUEST: + handleClientSideRPCRequest(context, msg); + reportActivity(); + break; + } + } + } + + private void reportActivity() { + systemContext.getDeviceStateService().onDeviceActivity(deviceId); + } + + private void reportSessionOpen() { + systemContext.getDeviceStateService().onDeviceConnect(deviceId); + } + + private void reportSessionClose() { + systemContext.getDeviceStateService().onDeviceDisconnect(deviceId); + } + + private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) { + GetAttributesRequest request = (GetAttributesRequest) src.getPayload(); + ListenableFuture> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames()); + ListenableFuture> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames()); + + Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback>>() { + @Override + public void onSuccess(@Nullable List> result) { + BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(), + request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1))); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress()); + } + + @Override + public void onFailure(Throwable t) { + if (t instanceof Exception) { + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress()); + } else { + logger.error("[{}] Failed to process attributes request", deviceId, t); + } + } + }); + } + + private ListenableFuture> getAttributeKvEntries(DeviceId deviceId, String scope, Optional> names) { + if (names.isPresent()) { + if (!names.get().isEmpty()) { + return systemContext.getAttributesService().find(deviceId, scope, names.get()); + } else { + return systemContext.getAttributesService().findAll(deviceId, scope); + } + } else { + return Futures.immediateFuture(Collections.emptyList()); + } + } + + private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) { + AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload(); + + JsonObject json = new JsonObject(); + for (AttributeKvEntry kv : request.getAttributes()) { + kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + } + + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), + SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1); + pushToRuleEngineWithTimeout(context, tbMsg, msgData); + } + + private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) { + TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload(); + + Map> tsData = request.getData(); + + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), + SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size()); + + for (Map.Entry> entry : tsData.entrySet()) { + JsonObject json = new JsonObject(); + for (KvEntry kv : entry.getValue()) { + kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v)); + } + TbMsgMetaData metaData = defaultMetaData.copy(); + metaData.putValue("ts", entry.getKey() + ""); + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); + pushToRuleEngineWithTimeout(context, tbMsg, msgData); + } + } + + private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) { + ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload(); + + JsonObject json = new JsonObject(); + json.addProperty("method", request.getMethod()); + json.add("params", jsonParser.parse(request.getParams())); + + TbMsgMetaData requestMetaData = defaultMetaData.copy(); + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId())); + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1); + pushToRuleEngineWithTimeout(context, tbMsg, msgData); + + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout()); + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress())); + } + + public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) { + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId()); + if (data != null) { + logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId()); + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer()); + } + } + + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) { + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId()); + if (data != null) { + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer()); + } + } + + private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) { + SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType(); + int requestId = pendingMsgData.getRequestId(); + if (systemContext.isQueuePersistenceEnabled()) { + pendingMsgs.put(tbMsg.getId(), pendingMsgData); + scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout()); + } else { + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId()); + sendMsgToSessionActor(response, pendingMsgData.getServerAddress()); + } + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self()); } void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { - refreshAttributes(msg); if (attributeSubscriptions.size() > 0) { ToDeviceMsg notification = null; if (msg.isDeleted()) { @@ -221,7 +435,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (notification != null) { ToDeviceMsg finalNotification = notification; attributeSubscriptions.entrySet().forEach(sub -> { - ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(finalNotification, sub.getKey()); + ActorSystemToDeviceSessionActorMsg response = new BasicActorSystemToDeviceSessionActorMsg(finalNotification, sub.getKey()); sendMsgToSessionActor(response, sub.getValue().getServer()); }); } @@ -230,50 +444,30 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - void process(ActorContext context, RuleChainDeviceMsg srcMsg) { - ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(), - srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self()); - ChainProcessingContext ctx = new ChainProcessingContext(md); - if (ctx.getChainLength() > 0) { - RuleProcessingMsg msg = new RuleProcessingMsg(ctx); - ActorRef ruleActorRef = ctx.getCurrentActor(); - ruleActorRef.tell(msg, ActorRef.noSender()); - } else { - context.self().tell(new RulesProcessedMsg(ctx), context.self()); - } - } - - void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { + private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) { SessionId sessionId = msg.getSessionId(); FromDeviceMsg inMsg = msg.getPayload(); - if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) { + if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) { logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg; - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId()); + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); boolean success = requestMd != null; if (success) { - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), responseMsg.getData()); - Optional pluginServerAddress = requestMd.getMsg().getServerAddress(); - if (pluginServerAddress.isPresent()) { - systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg); - logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); - } else { - context.parent().tell(responsePluginMsg, ActorRef.noSender()); - logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId()); - } + systemContext.getDeviceRpcService().processRpcResponseFromDevice(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), + requestMd.getMsg().getServerAddress(), responseMsg.getData(), null)); } else { logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); } if (msg.getSessionType() == SessionType.SYNC) { BasicCommandAckResponse response = success - ? BasicCommandAckResponse.onSuccess(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId()) - : BasicCommandAckResponse.onError(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); - sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); + ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId()) + : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); } } } - public void processClusterEventMsg(ClusterEventMsg msg) { + void processClusterEventMsg(ClusterEventMsg msg) { if (!msg.isAdded()) { logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); Predicate> filter = e -> e.getValue().getServer() @@ -283,102 +477,79 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data) { - return toPluginRpcResponseMsg(requestMsg, data, null); - } - - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, RpcError error) { - return toPluginRpcResponseMsg(requestMsg, null, error); - } - - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data, RpcError error) { - return new ToPluginRpcResponseDeviceMsg( - requestMsg.getPluginId(), - requestMsg.getPluginTenantId(), - new FromDeviceRpcResponse(requestMsg.getMsg().getId(), - data, - error - ) - ); - } - - void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) { - ChainProcessingContext ctx = msg.getCtx(); - ToDeviceActorMsg inMsg = ctx.getInMsg(); - SessionId sid = inMsg.getSessionId(); - ToDeviceSessionActorMsg response; - if (ctx.getResponse() != null) { - response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid); - } else { - response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid); - } - sendMsgToSessionActor(response, inMsg.getServerAddress()); - } - - private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { + private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) { SessionId sessionId = msg.getSessionId(); SessionType sessionType = msg.getSessionType(); FromDeviceMsg inMsg = msg.getPayload(); - if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { + if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); attributeSubscriptions.remove(sessionId); - } else if (inMsg.getMsgType() == MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { + } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress()); - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); rpcSubscriptions.remove(sessionId); } } - private void processSessionStateMsgs(ToDeviceActorMsg msg) { + private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) { SessionId sessionId = msg.getSessionId(); FromDeviceMsg inMsg = msg.getPayload(); if (inMsg instanceof SessionOpenMsg) { logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); + if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) { + SessionId sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null); + if (sessionIdToRemove != null) { + closeSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); + } + } sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress())); + if (sessions.size() == 1) { + reportSessionOpen(); + } } else if (inMsg instanceof SessionCloseMsg) { logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); sessions.remove(sessionId); attributeSubscriptions.remove(sessionId); rpcSubscriptions.remove(sessionId); + if (sessions.isEmpty()) { + reportSessionClose(); + } } } - private void sendMsgToSessionActor(ToDeviceSessionActorMsg response, Optional sessionAddress) { + private void sendMsgToSessionActor(ActorSystemToDeviceSessionActorMsg response, Optional sessionAddress) { if (sessionAddress.isPresent()) { ServerAddress address = sessionAddress.get(); logger.debug("{} Forwarding msg: {}", address, response); - systemContext.getRpcService().tell(sessionAddress.get(), response); + systemContext.getRpcService().tell(systemContext.getEncodingService() + .convertToProtoDataMessage(sessionAddress.get(), response)); } else { systemContext.getSessionManagerActor().tell(response, ActorRef.noSender()); } } - private List fetchAttributes(String scope) { - try { - //TODO: replace this with async operation. Happens only during actor creation, but is still criticla for performance, - return systemContext.getAttributesService().findAll(this.deviceId, scope).get(); - } catch (InterruptedException | ExecutionException e) { - logger.warning("[{}] Failed to fetch attributes for scope: {}", deviceId, scope); - throw new RuntimeException(e); - } - } - - public void processCredentialsUpdate() { - sessions.forEach((k, v) -> { - sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer()); - }); + void processCredentialsUpdate() { + sessions.forEach(this::closeSession); attributeSubscriptions.clear(); rpcSubscriptions.clear(); } - public void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { + private void closeSession(SessionId sessionId, SessionInfo sessionInfo) { + sendMsgToSessionActor(new BasicActorSystemToDeviceSessionActorMsg(new SessionCloseNotification(), sessionId), sessionInfo.getServer()); + } + + void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { this.deviceName = msg.getDeviceName(); this.deviceType = msg.getDeviceType(); + this.defaultMetaData = new TbMsgMetaData(); + this.defaultMetaData.putValue("deviceName", deviceName); + this.defaultMetaData.putValue("deviceType", deviceType); } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java new file mode 100644 index 0000000000..adc5531da5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2018 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.actors.device; + +import akka.actor.ActorRef; +import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 15.03.18. + */ +@Data +public final class DeviceActorToRuleEngineMsg implements TbActorMsg { + + private final ActorRef callbackRef; + private final TbMsg tbMsg; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG; + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java new file mode 100644 index 0000000000..23ad966049 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/device/PendingSessionMsgData.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2018 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.actors.device; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.session.SessionMsgType; + +import java.util.Optional; + +/** + * Created by ashvayka on 17.04.18. + */ +@Data +@AllArgsConstructor +public final class PendingSessionMsgData { + + private final SessionId sessionId; + private final Optional serverAddress; + private final SessionMsgType sessionMsgType; + private final int requestId; + private final boolean replyOnQueueAck; + private int ackMsgCount; + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java new file mode 100644 index 0000000000..0b6fe54c4a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/device/RuleEngineQueuePutAckMsg.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2018 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.actors.device; + +import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; + +import java.util.UUID; + +/** + * Created by ashvayka on 15.03.18. + */ +@Data +public final class RuleEngineQueuePutAckMsg implements TbActorMsg { + + private final UUID id; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG; + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java index e039b9634f..04c457c92e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java @@ -16,7 +16,6 @@ package org.thingsboard.server.actors.device; import lombok.Data; -import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.session.SessionType; diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java index 01342fd866..8a4262c5b2 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java @@ -16,13 +16,13 @@ package org.thingsboard.server.actors.device; import lombok.Data; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; /** * @author Andrew Shvayka */ @Data public class ToDeviceRpcRequestMetadata { - private final ToDeviceRpcRequestPluginMsg msg; + private final ToDeviceRpcRequestActorMsg msg; private final boolean sent; } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java similarity index 65% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java rename to application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java index 91abaaec9d..f82a8c2481 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/RpcMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java @@ -13,23 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.rpc; +package org.thingsboard.server.actors.device; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; +import lombok.Data; +import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.session.SessionType; + +import java.util.Optional; /** * @author Andrew Shvayka */ -@ToString -@RequiredArgsConstructor -public class RpcMsg { - @Getter - private final ServerAddress serverAddress; - @Getter - private final int msgClazz; - @Getter - private final byte[] msgData; +@Data +public class ToServerRpcRequestMetadata { + private final SessionId sessionId; + private final SessionType type; + private final Optional server; } diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java deleted file mode 100644 index 265da386ff..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActor.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ComponentActor; -import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.stats.StatsPersistTick; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -public class PluginActor extends ComponentActor { - - private PluginActor(ActorSystemContext systemContext, TenantId tenantId, PluginId pluginId) { - super(systemContext, tenantId, pluginId); - setProcessor(new PluginActorMessageProcessor(tenantId, pluginId, systemContext, - logger, context().parent(), context().self())); - } - - @Override - public void onReceive(Object msg) throws Exception { - if (msg instanceof PluginWebsocketMsg) { - onWebsocketMsg((PluginWebsocketMsg) msg); - } else if (msg instanceof PluginRestMsg) { - onRestMsg((PluginRestMsg) msg); - } else if (msg instanceof PluginCallbackMessage) { - onPluginCallback((PluginCallbackMessage) msg); - } else if (msg instanceof RuleToPluginMsgWrapper) { - onRuleToPluginMsg((RuleToPluginMsgWrapper) msg); - } else if (msg instanceof PluginRpcMsg) { - onRpcMsg((PluginRpcMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - onClusterEventMsg((ClusterEventMsg) msg); - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof ToPluginRpcResponseDeviceMsg) { - onRpcResponse((ToPluginRpcResponseDeviceMsg) msg); - } else if (msg instanceof PluginTerminationMsg) { - logger.info("[{}][{}] Going to terminate plugin actor.", tenantId, id); - context().parent().tell(msg, ActorRef.noSender()); - context().stop(self()); - } else if (msg instanceof TimeoutMsg) { - onTimeoutMsg(context(), (TimeoutMsg) msg); - } else if (msg instanceof StatsPersistTick) { - onStatsPersistTick(id); - } else { - logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName()); - } - } - - private void onPluginCallback(PluginCallbackMessage msg) { - try { - processor.onPluginCallbackMsg(msg); - } catch (Exception e) { - logAndPersist("onPluginCallbackMsg", e); - } - } - - private void onTimeoutMsg(ActorContext context, TimeoutMsg msg) { - processor.onTimeoutMsg(context, msg); - } - - private void onRpcResponse(ToPluginRpcResponseDeviceMsg msg) { - processor.onDeviceRpcMsg(msg.getResponse()); - } - - private void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException { - logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); - try { - processor.onRuleToPluginMsg(msg); - increaseMessagesProcessedCount(); - } catch (Exception e) { - logAndPersist("onRuleMsg", e); - } - } - - private void onWebsocketMsg(PluginWebsocketMsg msg) { - logger.debug("[{}] Going to process web socket msg: {}", id, msg); - try { - processor.onWebsocketMsg(msg); - increaseMessagesProcessedCount(); - } catch (Exception e) { - logAndPersist("onWebsocketMsg", e); - } - } - - private void onRestMsg(PluginRestMsg msg) { - logger.debug("[{}] Going to process rest msg: {}", id, msg); - try { - processor.onRestMsg(msg); - increaseMessagesProcessedCount(); - } catch (Exception e) { - logAndPersist("onRestMsg", e); - } - } - - private void onRpcMsg(PluginRpcMsg msg) { - try { - logger.debug("[{}] Going to process rpc msg: {}", id, msg); - processor.onRpcMsg(msg); - } catch (Exception e) { - logAndPersist("onRpcMsg", e); - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; - - private final TenantId tenantId; - private final PluginId pluginId; - - public ActorCreator(ActorSystemContext context, TenantId tenantId, PluginId pluginId) { - super(context); - this.tenantId = tenantId; - this.pluginId = pluginId; - } - - @Override - public PluginActor create() throws Exception { - return new PluginActor(context, tenantId, pluginId); - } - } - - @Override - protected long getErrorPersistFrequency() { - return systemContext.getPluginErrorPersistFrequency(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java deleted file mode 100644 index 2af30d4b49..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginActorMessageProcessor.java +++ /dev/null @@ -1,251 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.event.LoggingAdapter; -import com.fasterxml.jackson.core.JsonProcessingException; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.ComponentMsgProcessor; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; -import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; -import org.thingsboard.server.common.msg.session.MsgType; -import org.thingsboard.server.extensions.api.plugins.Plugin; -import org.thingsboard.server.extensions.api.plugins.PluginInitializationException; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -/** - * @author Andrew Shvayka - */ -public class PluginActorMessageProcessor extends ComponentMsgProcessor { - - private final SharedPluginProcessingContext pluginCtx; - private final PluginProcessingContext trustedCtx; - private PluginMetaData pluginMd; - private Plugin pluginImpl; - private ComponentLifecycleState state; - - - protected PluginActorMessageProcessor(TenantId tenantId, PluginId pluginId, ActorSystemContext systemContext - , LoggingAdapter logger, ActorRef parent, ActorRef self) { - super(systemContext, logger, tenantId, pluginId); - this.pluginCtx = new SharedPluginProcessingContext(systemContext, tenantId, pluginId, parent, self); - this.trustedCtx = new PluginProcessingContext(pluginCtx, null); - } - - @Override - public void start() throws Exception { - logger.info("[{}] Going to start plugin actor.", entityId); - pluginMd = systemContext.getPluginService().findPluginById(entityId); - if (pluginMd == null) { - throw new PluginInitializationException("Plugin not found!"); - } - if (pluginMd.getConfiguration() == null) { - throw new PluginInitializationException("Plugin metadata is empty!"); - } - state = pluginMd.getState(); - if (state == ComponentLifecycleState.ACTIVE) { - logger.info("[{}] Plugin is active. Going to initialize plugin.", entityId); - initComponent(); - } else { - logger.info("[{}] Plugin is suspended. Skipping plugin initialization.", entityId); - } - } - - @Override - public void stop() throws Exception { - onStop(); - } - - private void initComponent() { - try { - pluginImpl = initComponent(pluginMd.getClazz(), ComponentType.PLUGIN, mapper.writeValueAsString(pluginMd.getConfiguration())); - } catch (InstantiationException e) { - throw new PluginInitializationException("No default constructor for plugin implementation!", e); - } catch (IllegalAccessException e) { - throw new PluginInitializationException("Illegal Access Exception during plugin initialization!", e); - } catch (ClassNotFoundException e) { - throw new PluginInitializationException("Plugin Class not found!", e); - } catch (JsonProcessingException e) { - throw new PluginInitializationException("Plugin Configuration is invalid!", e); - } catch (Exception e) { - throw new PluginInitializationException(e.getMessage(), e); - } - } - - public void onRuleToPluginMsg(RuleToPluginMsgWrapper msg) throws RuleException { - if (state == ComponentLifecycleState.ACTIVE) { - try { - pluginImpl.process(trustedCtx, msg.getRuleTenantId(), msg.getRuleId(), msg.getMsg()); - } catch (Exception ex) { - logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex); - RuleToPluginMsg ruleMsg = msg.getMsg(); - MsgType responceMsgType = MsgType.RULE_ENGINE_ERROR; - Integer requestId = 0; - if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) { - requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId(); - } - trustedCtx.reply( - new ResponsePluginToRuleMsg(ruleMsg.getUid(), tenantId, msg.getRuleId(), - BasicStatusCodeResponse.onError(responceMsgType, requestId, ex))); - } - } else { - //TODO: reply with plugin suspended message - } - } - - public void onWebsocketMsg(PluginWebsocketMsg msg) { - if (state == ComponentLifecycleState.ACTIVE) { - pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg); - } else { - //TODO: reply with plugin suspended message - } - } - - public void onRestMsg(PluginRestMsg msg) { - if (state == ComponentLifecycleState.ACTIVE) { - pluginImpl.process(new PluginProcessingContext(pluginCtx, msg.getSecurityCtx()), msg); - } - } - - public void onRpcMsg(PluginRpcMsg msg) { - if (state == ComponentLifecycleState.ACTIVE) { - pluginImpl.process(trustedCtx, msg.getRpcMsg()); - } else { - //TODO: reply with plugin suspended message - } - } - - public void onPluginCallbackMsg(PluginCallbackMessage msg) { - if (state == ComponentLifecycleState.ACTIVE) { - if (msg.isSuccess()) { - msg.getCallback().onSuccess(trustedCtx, msg.getV()); - } else { - msg.getCallback().onFailure(trustedCtx, msg.getE()); - } - } else { - //TODO: reply with plugin suspended message - } - } - - - public void onTimeoutMsg(ActorContext context, TimeoutMsg msg) { - if (state == ComponentLifecycleState.ACTIVE) { - pluginImpl.process(trustedCtx, msg); - } - } - - - public void onDeviceRpcMsg(FromDeviceRpcResponse response) { - if (state == ComponentLifecycleState.ACTIVE) { - pluginImpl.process(trustedCtx, response); - } - } - - @Override - public void onClusterEventMsg(ClusterEventMsg msg) { - if (state == ComponentLifecycleState.ACTIVE) { - ServerAddress address = msg.getServerAddress(); - if (msg.isAdded()) { - logger.debug("[{}] Going to process server add msg: {}", entityId, address); - pluginImpl.onServerAdded(trustedCtx, address); - } else { - logger.debug("[{}] Going to process server remove msg: {}", entityId, address); - pluginImpl.onServerRemoved(trustedCtx, address); - } - } - } - - @Override - public void onCreated(ActorContext context) { - logger.info("[{}] Going to process onCreated plugin.", entityId); - } - - @Override - public void onUpdate(ActorContext context) throws Exception { - PluginMetaData oldPluginMd = pluginMd; - pluginMd = systemContext.getPluginService().findPluginById(entityId); - boolean requiresRestart = false; - logger.info("[{}] Plugin configuration was updated from {} to {}.", entityId, oldPluginMd, pluginMd); - if (!oldPluginMd.getClazz().equals(pluginMd.getClazz())) { - logger.info("[{}] Plugin requires restart due to clazz change from {} to {}.", - entityId, oldPluginMd.getClazz(), pluginMd.getClazz()); - requiresRestart = true; - } else if (!oldPluginMd.getConfiguration().equals(pluginMd.getConfiguration())) { - logger.info("[{}] Plugin requires restart due to configuration change from {} to {}.", - entityId, oldPluginMd.getConfiguration(), pluginMd.getConfiguration()); - requiresRestart = true; - } - if (requiresRestart) { - this.state = ComponentLifecycleState.SUSPENDED; - if (pluginImpl != null) { - pluginImpl.stop(trustedCtx); - } - start(); - } - } - - @Override - public void onStop(ActorContext context) { - onStop(); - scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay()); - } - - private void onStop() { - logger.info("[{}] Going to process onStop plugin.", entityId); - this.state = ComponentLifecycleState.SUSPENDED; - if (pluginImpl != null) { - pluginImpl.stop(trustedCtx); - } - } - - @Override - public void onActivate(ActorContext context) throws Exception { - logger.info("[{}] Going to process onActivate plugin.", entityId); - this.state = ComponentLifecycleState.ACTIVE; - if (pluginImpl != null) { - pluginImpl.resume(trustedCtx); - logger.info("[{}] Plugin resumed.", entityId); - } else { - start(); - } - } - - @Override - public void onSuspend(ActorContext context) { - logger.info("[{}] Going to process onSuspend plugin.", entityId); - this.state = ComponentLifecycleState.SUSPENDED; - if (pluginImpl != null) { - pluginImpl.suspend(trustedCtx); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java deleted file mode 100644 index 539a874d77..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginCallbackMessage.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import lombok.Data; -import lombok.Getter; -import lombok.ToString; -import org.thingsboard.server.extensions.api.plugins.PluginCallback; - -import java.util.Optional; - -/** - * @author Andrew Shvayka - */ -@ToString -public final class PluginCallbackMessage { - @Getter - private final PluginCallback callback; - @Getter - private final boolean success; - @Getter - private final V v; - @Getter - private final Exception e; - - public static PluginCallbackMessage onSuccess(PluginCallback callback, V data) { - return new PluginCallbackMessage(true, callback, data, null); - } - - public static PluginCallbackMessage onError(PluginCallback callback, Exception e) { - return new PluginCallbackMessage(false, callback, null, e); - } - - private PluginCallbackMessage(boolean success, PluginCallback callback, V v, Exception e) { - this.success = success; - this.callback = callback; - this.v = v; - this.e = e; - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java deleted file mode 100644 index ce95ee4629..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java +++ /dev/null @@ -1,546 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import akka.actor.ActorRef; -import com.google.common.base.Function; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.common.data.kv.AttributeKey; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.kv.TsKvQuery; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.PluginCallback; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.msg.*; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; - -@Slf4j -public final class PluginProcessingContext implements PluginContext { - - private static final Executor executor = Executors.newSingleThreadExecutor(); - public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; - public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; - public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; - - private final SharedPluginProcessingContext pluginCtx; - private final Optional securityCtx; - - public PluginProcessingContext(SharedPluginProcessingContext pluginCtx, PluginApiCallSecurityContext securityCtx) { - super(); - this.pluginCtx = pluginCtx; - this.securityCtx = Optional.ofNullable(securityCtx); - } - - public void persistError(String method, Exception e) { - pluginCtx.persistError(method, e); - } - - @Override - public void sendPluginRpcMsg(RpcMsg msg) { - this.pluginCtx.rpcService.tell(new PluginRpcMsg(pluginCtx.tenantId, pluginCtx.pluginId, msg)); - } - - @Override - public void send(PluginWebsocketMsg wsMsg) throws IOException { - pluginCtx.msgEndpoint.send(wsMsg); - } - - @Override - public void close(PluginWebsocketSessionRef sessionRef) throws IOException { - pluginCtx.msgEndpoint.close(sessionRef); - } - - @Override - public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List attributes, final PluginCallback callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> futures = pluginCtx.attributesService.save(entityId, scope, attributes); - Futures.addCallback(futures, getListCallback(callback, v -> { - if (entityId.getEntityType() == EntityType.DEVICE) { - onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes); - } - return null; - }), executor); - })); - } - - @Override - public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List keys, final PluginCallback callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> futures = pluginCtx.attributesService.removeAll(entityId, scope, keys); - Futures.addCallback(futures, getCallback(callback, v -> null), executor); - if (entityId.getEntityType() == EntityType.DEVICE) { - onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet())); - } - })); - } - - @Override - public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void loadAttributes(EntityId entityId, String attributeType, Collection attributeKeys, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void loadAttributes(EntityId entityId, String attributeType, PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> future = pluginCtx.attributesService.findAll(entityId, attributeType); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - List>> futures = new ArrayList<>(); - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType))); - convertFuturesAndAddCallback(callback, futures); - })); - } - - @Override - public void loadAttributes(final EntityId entityId, final Collection attributeTypes, final Collection attributeKeys, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - List>> futures = new ArrayList<>(); - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys))); - convertFuturesAndAddCallback(callback, futures); - })); - } - - @Override - public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entry); - Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); - })); - } - - @Override - public void saveTsData(final EntityId entityId, final List entries, final PluginCallback callback) { - saveTsData(entityId, entries, 0L, callback); - } - - @Override - public void saveTsData(final EntityId entityId, final List entries, long ttl, final PluginCallback callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> rsListFuture = pluginCtx.tsService.save(entityId, entries, ttl); - Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor); - })); - } - - - @Override - public void loadTimeseries(final EntityId entityId, final List queries, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> future = pluginCtx.tsService.findAll(entityId, queries); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void loadLatestTimeseries(final EntityId entityId, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> future = pluginCtx.tsService.findAllLatest(entityId); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void logAttributesUpdated(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, - List attributes, Exception e) { - pluginCtx.auditLogService.logEntityAction( - ctx.getTenantId(), - ctx.getCustomerId(), - ctx.getUserId(), - ctx.getUserName(), - (UUIDBased & EntityId)entityId, - null, - ActionType.ATTRIBUTES_UPDATED, - e, - attributeType, - attributes); - } - - @Override - public void logAttributesDeleted(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e) { - pluginCtx.auditLogService.logEntityAction( - ctx.getTenantId(), - ctx.getCustomerId(), - ctx.getUserId(), - ctx.getUserName(), - (UUIDBased & EntityId)entityId, - null, - ActionType.ATTRIBUTES_DELETED, - e, - attributeType, - keys); - } - - @Override - public void logAttributesRead(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e) { - pluginCtx.auditLogService.logEntityAction( - ctx.getTenantId(), - ctx.getCustomerId(), - ctx.getUserId(), - ctx.getUserName(), - (UUIDBased & EntityId)entityId, - null, - ActionType.ATTRIBUTES_READ, - e, - attributeType, - keys); - } - - @Override - public void loadLatestTimeseries(final EntityId entityId, final Collection keys, final PluginCallback> callback) { - validate(entityId, new ValidationCallback(callback, ctx -> { - ListenableFuture> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys); - Futures.addCallback(rsListFuture, getCallback(callback, v -> v), executor); - })); - } - - @Override - public void reply(PluginToRuleMsg msg) { - pluginCtx.parentActor.tell(msg, ActorRef.noSender()); - } - - @Override - public PluginId getPluginId() { - return pluginCtx.pluginId; - } - - @Override - public Optional getSecurityCtx() { - return securityCtx; - } - - private void onDeviceAttributesDeleted(TenantId tenantId, DeviceId deviceId, Set keys) { - pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onDelete(tenantId, deviceId, keys)); - } - - private void onDeviceAttributesChanged(TenantId tenantId, DeviceId deviceId, String scope, List values) { - pluginCtx.toDeviceActor(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, deviceId, scope, values)); - } - - private FutureCallback> getListCallback(final PluginCallback callback, Function, R> transformer) { - return new FutureCallback>() { - @Override - public void onSuccess(@Nullable List result) { - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender()); - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof Exception) { - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender()); - } else { - log.error("Critical error: {}", t.getMessage(), t); - } - } - }; - } - - private FutureCallback getCallback(final PluginCallback callback, Function transformer) { - return new FutureCallback() { - @Override - public void onSuccess(@Nullable R result) { - try { - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, transformer.apply(result)), ActorRef.noSender()); - } catch (Exception e) { - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, e), ActorRef.noSender()); - } - } - - @Override - public void onFailure(Throwable t) { - if (t instanceof Exception) { - pluginCtx.self().tell(PluginCallbackMessage.onError(callback, (Exception) t), ActorRef.noSender()); - } else { - log.error("Critical error: {}", t.getMessage(), t); - } - } - }; - } - - @Override - public void checkAccess(DeviceId deviceId, PluginCallback callback) { - validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null))); - } - - private void validate(EntityId entityId, ValidationCallback callback) { - if (securityCtx.isPresent()) { - final PluginApiCallSecurityContext ctx = securityCtx.get(); - switch (entityId.getEntityType()) { - case DEVICE: - validateDevice(ctx, entityId, callback); - return; - case ASSET: - validateAsset(ctx, entityId, callback); - return; - case RULE: - validateRule(ctx, entityId, callback); - return; - case PLUGIN: - validatePlugin(ctx, entityId, callback); - return; - case CUSTOMER: - validateCustomer(ctx, entityId, callback); - return; - case TENANT: - validateTenant(ctx, entityId, callback); - return; - default: - //TODO: add support of other entities - throw new IllegalStateException("Not Implemented!"); - } - } else { - callback.onSuccess(this, ValidationResult.ok()); - } - } - - private void validateDevice(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isSystemAdmin()) { - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else { - ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); - Futures.addCallback(deviceFuture, getCallback(callback, device -> { - if (device == null) { - return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND); - } else { - if (!device.getTenantId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); - } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) { - return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); - } else { - return ValidationResult.ok(); - } - } - })); - } - } - - private void validateAsset(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isSystemAdmin()) { - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else { - ListenableFuture assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId())); - Futures.addCallback(assetFuture, getCallback(callback, asset -> { - if (asset == null) { - return ValidationResult.entityNotFound("Asset with requested id wasn't found!"); - } else { - if (!asset.getTenantId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!"); - } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) { - return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); - } else { - return ValidationResult.ok(); - } - } - })); - } - } - - private void validateRule(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isCustomerUser()) { - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else { - ListenableFuture ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId())); - Futures.addCallback(ruleFuture, getCallback(callback, rule -> { - if (rule == null) { - return ValidationResult.entityNotFound("Rule with requested id wasn't found!"); - } else { - if (ctx.isTenantAdmin() && !rule.getTenantId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Rule doesn't belong to the current Tenant!"); - } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) { - return ValidationResult.accessDenied("Rule is not in system scope!"); - } else { - return ValidationResult.ok(); - } - } - })); - } - } - - private void validatePlugin(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isCustomerUser()) { - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else { - ListenableFuture pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId())); - Futures.addCallback(pluginFuture, getCallback(callback, plugin -> { - if (plugin == null) { - return ValidationResult.entityNotFound("Plugin with requested id wasn't found!"); - } else { - if (ctx.isTenantAdmin() && !plugin.getTenantId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Plugin doesn't belong to the current Tenant!"); - } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) { - return ValidationResult.accessDenied("Plugin is not in system scope!"); - } else { - return ValidationResult.ok(); - } - } - })); - } - } - - private void validateCustomer(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isSystemAdmin()) { - callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else { - ListenableFuture customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId())); - Futures.addCallback(customerFuture, getCallback(callback, customer -> { - if (customer == null) { - return ValidationResult.entityNotFound("Customer with requested id wasn't found!"); - } else { - if (!customer.getTenantId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!"); - } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) { - return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); - } else { - return ValidationResult.ok(); - } - } - })); - } - } - - private void validateTenant(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { - if (ctx.isCustomerUser()) { - callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); - } else if (ctx.isSystemAdmin()) { - callback.onSuccess(this, ValidationResult.ok()); - } else { - ListenableFuture tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); - Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { - if (tenant == null) { - return ValidationResult.entityNotFound("Tenant with requested id wasn't found!"); - } else if (!tenant.getId().equals(ctx.getTenantId())) { - return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); - } else { - return ValidationResult.ok(); - } - })); - } - } - - @Override - public ListenableFuture> findByFromAndType(EntityId from, String relationType) { - return this.pluginCtx.relationService.findByFromAndTypeAsync(from, relationType, RelationTypeGroup.COMMON); - } - - @Override - public ListenableFuture> findByToAndType(EntityId from, String relationType) { - return this.pluginCtx.relationService.findByToAndTypeAsync(from, relationType, RelationTypeGroup.COMMON); - } - - @Override - public Optional resolve(EntityId entityId) { - return pluginCtx.routingService.resolveById(entityId); - } - - @Override - public void getDevice(DeviceId deviceId, PluginCallback callback) { - ListenableFuture deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId); - Futures.addCallback(deviceFuture, getCallback(callback, v -> v)); - } - - @Override - public void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback> callback) { - //TODO: add caching here with async api. - List devices = pluginCtx.deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, new TextPageLink(limit)).getData(); - pluginCtx.self().tell(PluginCallbackMessage.onSuccess(callback, devices), ActorRef.noSender()); - } - - @Override - public void sendRpcRequest(ToDeviceRpcRequest msg) { - pluginCtx.sendRpcRequest(msg); - } - - @Override - public void logRpcRequest(PluginApiCallSecurityContext ctx, DeviceId deviceId, ToDeviceRpcRequestBody body, boolean oneWay, Optional rpcError, Exception e) { - String rpcErrorStr = ""; - if (rpcError.isPresent()) { - rpcErrorStr = "RPC Error: " + rpcError.get().name(); - } - String method = body.getMethod(); - String params = body.getParams(); - pluginCtx.auditLogService.logEntityAction( - ctx.getTenantId(), - ctx.getCustomerId(), - ctx.getUserId(), - ctx.getUserName(), - deviceId, - null, - ActionType.RPC_CALL, - e, - rpcErrorStr, - new Boolean(oneWay), - method, - params); - } - - @Override - public void scheduleTimeoutMsg(TimeoutMsg msg) { - pluginCtx.scheduleTimeoutMsg(msg); - } - - - private void convertFuturesAndAddCallback(PluginCallback> callback, List>> futures) { - ListenableFuture> future = Futures.transform(Futures.successfulAsList(futures), - (Function>, ? extends List>) input -> { - List result = new ArrayList<>(); - input.forEach(r -> result.addAll(r)); - return result; - }, executor); - Futures.addCallback(future, getCallback(callback, v -> v), executor); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java deleted file mode 100644 index 99b1e64d0a..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginTerminationMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import org.thingsboard.server.actors.shared.ActorTerminationMsg; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.SessionId; - -/** - * @author Andrew Shvayka - */ -public class PluginTerminationMsg extends ActorTerminationMsg { - - public PluginTerminationMsg(PluginId id) { - super(id); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java b/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java deleted file mode 100644 index 4b2268cd57..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/RuleToPluginMsgWrapper.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.aware.RuleAwareMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; - -public class RuleToPluginMsgWrapper implements ToPluginActorMsg, RuleAwareMsg { - - private final TenantId pluginTenantId; - private final PluginId pluginId; - private final TenantId ruleTenantId; - private final RuleId ruleId; - private final RuleToPluginMsg msg; - - public RuleToPluginMsgWrapper(TenantId pluginTenantId, PluginId pluginId, TenantId ruleTenantId, RuleId ruleId, RuleToPluginMsg msg) { - super(); - this.pluginTenantId = pluginTenantId; - this.pluginId = pluginId; - this.ruleTenantId = ruleTenantId; - this.ruleId = ruleId; - this.msg = msg; - } - - @Override - public TenantId getPluginTenantId() { - return pluginTenantId; - } - - @Override - public PluginId getPluginId() { - return pluginId; - } - - public TenantId getRuleTenantId() { - return ruleTenantId; - } - - @Override - public RuleId getRuleId() { - return ruleId; - } - - - public RuleToPluginMsg getMsg() { - return msg; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java deleted file mode 100644 index 06138a3308..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import akka.actor.ActorRef; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.audit.AuditLogService; -import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.rule.RuleService; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; -import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; -import scala.concurrent.duration.Duration; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; - -@Slf4j -public final class SharedPluginProcessingContext { - final ActorRef parentActor; - final ActorRef currentActor; - final ActorSystemContext systemContext; - final PluginWebSocketMsgEndpoint msgEndpoint; - final AssetService assetService; - final DeviceService deviceService; - final RuleService ruleService; - final PluginService pluginService; - final CustomerService customerService; - final TenantService tenantService; - final TimeseriesService tsService; - final AttributesService attributesService; - final ClusterRpcService rpcService; - final ClusterRoutingService routingService; - final RelationService relationService; - final AuditLogService auditLogService; - final PluginId pluginId; - final TenantId tenantId; - - public SharedPluginProcessingContext(ActorSystemContext sysContext, TenantId tenantId, PluginId pluginId, - ActorRef parentActor, ActorRef self) { - super(); - this.tenantId = tenantId; - this.pluginId = pluginId; - this.parentActor = parentActor; - this.currentActor = self; - this.systemContext = sysContext; - this.msgEndpoint = sysContext.getWsMsgEndpoint(); - this.tsService = sysContext.getTsService(); - this.attributesService = sysContext.getAttributesService(); - this.assetService = sysContext.getAssetService(); - this.deviceService = sysContext.getDeviceService(); - this.rpcService = sysContext.getRpcService(); - this.routingService = sysContext.getRoutingService(); - this.ruleService = sysContext.getRuleService(); - this.pluginService = sysContext.getPluginService(); - this.customerService = sysContext.getCustomerService(); - this.tenantService = sysContext.getTenantService(); - this.relationService = sysContext.getRelationService(); - this.auditLogService = sysContext.getAuditLogService(); - } - - public PluginId getPluginId() { - return pluginId; - } - - public TenantId getPluginTenantId() { - return tenantId; - } - - public void toDeviceActor(DeviceAttributesEventNotificationMsg msg) { - forward(msg.getDeviceId(), msg, rpcService::tell); - } - - public void sendRpcRequest(ToDeviceRpcRequest msg) { - log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg); - ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg); - forward(msg.getDeviceId(), rpcMsg, rpcService::tell); - } - - private void forward(DeviceId deviceId, T msg, BiConsumer rpcFunction) { - Optional instance = routingService.resolveById(deviceId); - if (instance.isPresent()) { - log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg); - rpcFunction.accept(instance.get(), msg); - } else { - log.trace("[{}] Forwarding msg {} to local device actor!", pluginId, msg); - parentActor.tell(msg, ActorRef.noSender()); - } - } - - public void scheduleTimeoutMsg(TimeoutMsg msg) { - log.debug("Scheduling msg {} with delay {} ms", msg, msg.getTimeout()); - systemContext.getScheduler().scheduleOnce( - Duration.create(msg.getTimeout(), TimeUnit.MILLISECONDS), - currentActor, - msg, - systemContext.getActorSystem().dispatcher(), - ActorRef.noSender()); - - } - - public void persistError(String method, Exception e) { - systemContext.persistError(tenantId, pluginId, method, e); - } - - public ActorRef self() { - return currentActor; - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java b/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java deleted file mode 100644 index f135ffbd53..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResult.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.plugin; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class ValidationResult { - - private final ValidationResultCode resultCode; - private final String message; - - public static ValidationResult ok() { - return new ValidationResult(ValidationResultCode.OK, "Ok"); - } - - public static ValidationResult accessDenied(String message) { - return new ValidationResult(ValidationResultCode.ACCESS_DENIED, message); - } - - public static ValidationResult entityNotFound(String message) { - return new ValidationResult(ValidationResultCode.ENTITY_NOT_FOUND, message); - } - - public static ValidationResult unauthorized(String message) { - return new ValidationResult(ValidationResultCode.UNAUTHORIZED, message); - } - - public static ValidationResult internalError(String message) { - return new ValidationResult(ValidationResultCode.INTERNAL_ERROR, message); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java index 4fa222755d..14bb636830 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/BasicRpcSessionListener.java @@ -17,43 +17,23 @@ package org.thingsboard.server.actors.rpc; import akka.actor.ActorRef; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.SerializationUtils; -import org.springframework.util.StringUtils; -import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ActorService; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.*; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.service.cluster.rpc.GrpcSession; import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; -import java.io.Serializable; -import java.util.UUID; - /** * @author Andrew Shvayka */ @Slf4j public class BasicRpcSessionListener implements GrpcSessionListener { - public static final String SESSION_RECEIVED_SESSION_ACTOR_MSG = "{} session [{}] received session actor msg {}"; - private final ActorSystemContext context; private final ActorService service; private final ActorRef manager; private final ActorRef self; - public BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) { - this.context = context; - this.service = context.getActorService(); + public BasicRpcSessionListener(ActorService service, ActorRef manager, ActorRef self) { + this.service = service; this.manager = manager; this.self = self; } @@ -73,47 +53,11 @@ public class BasicRpcSessionListener implements GrpcSessionListener { } @Override - public void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg) { - if (log.isTraceEnabled()) { - log.trace("{} session [{}] received plugin msg {}", getType(session), session.getRemoteServer(), msg); - } - service.onMsg(convert(session.getRemoteServer(), msg)); - } - - @Override - public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) { - log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg); - service.onMsg((ToDeviceActorMsg) deserialize(msg.getData().toByteArray())); - } - - @Override - public void onToDeviceActorNotificationRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg) { - log.trace("{} session [{}] received device actor notification msg {}", getType(session), session.getRemoteServer(), msg); - service.onMsg((ToDeviceActorNotificationMsg) deserialize(msg.getData().toByteArray())); - } - - @Override - public void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg) { - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg); - service.onMsg((ToDeviceSessionActorMsg) deserialize(msg.getData().toByteArray())); - } - - @Override - public void onToDeviceRpcRequestRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) { - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg); - service.onMsg(deserialize(session.getRemoteServer(), msg)); - } - - @Override - public void onFromDeviceRpcResponseRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) { - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg); - service.onMsg(deserialize(session.getRemoteServer(), msg)); - } - - @Override - public void onToAllNodesRpcMessage(GrpcSession session, ClusterAPIProtos.ToAllNodesRpcMessage msg) { - log.trace(SESSION_RECEIVED_SESSION_ACTOR_MSG, getType(session), session.getRemoteServer(), msg); - service.onMsg((ToAllNodesMsg) deserialize(msg.getData().toByteArray())); + public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) { + log.trace("{} Service [{}] received session actor msg {}", getType(session), + session.getRemoteServer(), + clusterMessage); + service.onReceivedMsg(session.getRemoteServer(), clusterMessage); } @Override @@ -127,45 +71,5 @@ public class BasicRpcSessionListener implements GrpcSessionListener { return session.isClient() ? "Client" : "Server"; } - private static PluginRpcMsg convert(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcMessage msg) { - ClusterAPIProtos.PluginAddress address = msg.getAddress(); - TenantId tenantId = new TenantId(toUUID(address.getTenantId())); - PluginId pluginId = new PluginId(toUUID(address.getPluginId())); - RpcMsg rpcMsg = new RpcMsg(serverAddress, msg.getClazz(), msg.getData().toByteArray()); - return new PluginRpcMsg(tenantId, pluginId, rpcMsg); - } - - private static UUID toUUID(ClusterAPIProtos.Uid uid) { - return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb()); - } - - private static ToDeviceRpcRequestPluginMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) { - ClusterAPIProtos.PluginAddress address = msg.getAddress(); - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId())); - PluginId pluginId = new PluginId(toUUID(address.getPluginId())); - - TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId())); - DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId())); - - ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams()); - ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), null, deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody); - - return new ToDeviceRpcRequestPluginMsg(serverAddress, pluginId, pluginTenantId, request); - } - - private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) { - ClusterAPIProtos.PluginAddress address = msg.getAddress(); - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId())); - PluginId pluginId = new PluginId(toUUID(address.getPluginId())); - - RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null; - FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error); - return new ToPluginRpcResponseDeviceMsg(pluginId, pluginTenantId, response); - } - - @SuppressWarnings("unchecked") - private static T deserialize(byte[] data) { - return (T) SerializationUtils.deserialize(data); - } } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java index 3718a22dfe..2dd949eb7f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java @@ -23,5 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos; */ @Data public final class RpcBroadcastMsg { - private final ClusterAPIProtos.ToRpcServerMessage msg; + private final ClusterAPIProtos.ClusterMessage msg; } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java index 9290a8fe58..3f3f70b424 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java @@ -23,12 +23,17 @@ import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.service.cluster.discovery.ServerInstance; -import java.util.*; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; /** * @author Andrew Shvayka @@ -39,7 +44,7 @@ public class RpcManagerActor extends ContextAwareActor { private final Map sessionActors; - private final Map> pendingMsgs; + private final Map> pendingMsgs; private final ServerAddress instance; @@ -56,10 +61,16 @@ public class RpcManagerActor extends ContextAwareActor { } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { - if (msg instanceof RpcSessionTellMsg) { - onMsg((RpcSessionTellMsg) msg); + if (msg instanceof ClusterAPIProtos.ClusterMessage) { + onMsg((ClusterAPIProtos.ClusterMessage) msg); } else if (msg instanceof RpcBroadcastMsg) { onMsg((RpcBroadcastMsg) msg); } else if (msg instanceof RpcSessionCreateRequestMsg) { @@ -77,24 +88,30 @@ public class RpcManagerActor extends ContextAwareActor { private void onMsg(RpcBroadcastMsg msg) { log.debug("Forwarding msg to session actors {}", msg); - sessionActors.keySet().forEach(address -> onMsg(new RpcSessionTellMsg(address, msg.getMsg()))); + sessionActors.keySet().forEach(address -> onMsg(msg.getMsg())); pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); } - private void onMsg(RpcSessionTellMsg msg) { - ServerAddress address = msg.getServerAddress(); - SessionActorInfo session = sessionActors.get(address); - if (session != null) { - log.debug("{} Forwarding msg to session actor", address); - session.actor.tell(msg, ActorRef.noSender()); - } else { - log.debug("{} Storing msg to pending queue", address); - Queue queue = pendingMsgs.get(address); - if (queue == null) { - queue = new LinkedList<>(); - pendingMsgs.put(address, queue); + private void onMsg(ClusterAPIProtos.ClusterMessage msg) { + if (msg.hasServerAddress()) { + ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), + msg.getServerAddress().getPort()); + SessionActorInfo session = sessionActors.get(address); + if (session != null) { + log.debug("{} Forwarding msg to session actor", address); + session.getActor().tell(msg, ActorRef.noSender()); + } else { + log.debug("{} Storing msg to pending queue", address); + Queue queue = pendingMsgs.get(address); + if (queue == null) { + queue = new LinkedList<>(); + pendingMsgs.put(new ServerAddress( + msg.getServerAddress().getHost(), msg.getServerAddress().getPort()), queue); + } + queue.add(msg); } - queue.add(msg.getMsg()); + } else { + logger.warning("Cluster msg doesn't have set Server Address [{}]", msg); } } @@ -141,7 +158,7 @@ public class RpcManagerActor extends ContextAwareActor { private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) { log.debug("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect); SessionActorInfo sessionRef = sessionActors.get(remoteAddress); - if (context().sender().equals(sessionRef.actor)) { + if (context().sender() != null && context().sender().equals(sessionRef.actor)) { sessionActors.remove(remoteAddress); pendingMsgs.remove(remoteAddress); if (reconnect) { @@ -160,10 +177,10 @@ public class RpcManagerActor extends ContextAwareActor { private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) { sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender)); log.debug("[{}][{}] Registering session actor.", remoteAddress, uuid); - Queue data = pendingMsgs.remove(remoteAddress); + Queue data = pendingMsgs.remove(remoteAddress); if (data != null) { log.debug("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size()); - data.forEach(msg -> sender.tell(new RpcSessionTellMsg(remoteAddress, msg), ActorRef.noSender())); + data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender())); } else { log.debug("[{}][{}] No pending messages to forward.", remoteAddress, uuid); } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java index db029fa13b..c9cf8696e5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionActor.java @@ -23,6 +23,7 @@ import io.grpc.stub.StreamObserver; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; @@ -31,6 +32,8 @@ import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; import java.util.UUID; +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE; + /** * @author Andrew Shvayka */ @@ -47,17 +50,23 @@ public class RpcSessionActor extends ContextAwareActor { this.sessionId = sessionId; } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { - if (msg instanceof RpcSessionTellMsg) { - tell((RpcSessionTellMsg) msg); + if (msg instanceof ClusterAPIProtos.ClusterMessage) { + tell((ClusterAPIProtos.ClusterMessage) msg); } else if (msg instanceof RpcSessionCreateRequestMsg) { initSession((RpcSessionCreateRequestMsg) msg); } } - private void tell(RpcSessionTellMsg msg) { - session.sendMsg(msg.getMsg()); + private void tell(ClusterAPIProtos.ClusterMessage msg) { + session.sendMsg(msg); } @Override @@ -69,7 +78,7 @@ public class RpcSessionActor extends ContextAwareActor { private void initSession(RpcSessionCreateRequestMsg msg) { log.info("[{}] Initializing session", context().self()); ServerAddress remoteServer = msg.getRemoteAddress(); - listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self()); + listener = new BasicRpcSessionListener(systemContext.getActorService(), context().parent(), context().self()); if (msg.getRemoteAddress() == null) { // Server session session = new GrpcSession(listener); @@ -84,7 +93,7 @@ public class RpcSessionActor extends ContextAwareActor { session.initInputStream(); ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel); - StreamObserver outputStream = stub.handlePluginMsgs(session.getInputStream()); + StreamObserver outputStream = stub.handleMsgs(session.getInputStream()); session.setOutputStream(outputStream); session.initOutputStream(); @@ -108,11 +117,10 @@ public class RpcSessionActor extends ContextAwareActor { } } - private ClusterAPIProtos.ToRpcServerMessage toConnectMsg() { + private ClusterAPIProtos.ClusterMessage toConnectMsg() { ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress(); - return ClusterAPIProtos.ToRpcServerMessage.newBuilder().setConnectMsg( - ClusterAPIProtos.ConnectRpcMessage.newBuilder().setServerAddress( - ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()).setPort(instance.getPort()).build()).build()).build(); - + return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress( + ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost()) + .setPort(instance.getPort()).build()).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java index 5bcf1d621b..0c1136eb0b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java @@ -30,6 +30,6 @@ public final class RpcSessionCreateRequestMsg { private final UUID msgUid; private final ServerAddress remoteAddress; - private final StreamObserver responseObserver; + private final StreamObserver responseObserver; } diff --git a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java index 5a61044f49..50db43f7fb 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java @@ -16,7 +16,6 @@ package org.thingsboard.server.actors.rpc; import lombok.Data; -import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; /** @@ -24,6 +23,5 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos; */ @Data public final class RpcSessionTellMsg { - private final ServerAddress serverAddress; - private final ClusterAPIProtos.ToRpcServerMessage msg; + private final ClusterAPIProtos.ClusterMessage msg; } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java deleted file mode 100644 index 8723a68516..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingContext.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import akka.actor.ActorRef; -import org.thingsboard.server.common.msg.core.RuleEngineError; -import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.device.DeviceAttributes; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; - -public class ChainProcessingContext { - - private final ChainProcessingMetaData md; - private final int index; - private final RuleEngineError error; - private ToDeviceMsg response; - - - public ChainProcessingContext(ChainProcessingMetaData md) { - super(); - this.md = md; - this.index = 0; - this.error = RuleEngineError.NO_RULES; - } - - private ChainProcessingContext(ChainProcessingContext other, int indexOffset, RuleEngineError error) { - super(); - this.md = other.md; - this.index = other.index + indexOffset; - this.error = error; - this.response = other.response; - - if (this.index < 0 || this.index >= this.md.chain.size()) { - throw new IllegalArgumentException("Can't apply offset " + indexOffset + " to the chain!"); - } - } - - public ActorRef getDeviceActor() { - return md.originator; - } - - public ActorRef getCurrentActor() { - return md.chain.getRuleActorMd(index).getActorRef(); - } - - public boolean hasNext() { - return (getChainLength() - 1) > index; - } - - public boolean isFailure() { - return (error != null && error.isCritical()) || (response != null && !response.isSuccess()); - } - - public ChainProcessingContext getNext() { - return new ChainProcessingContext(this, 1, this.error); - } - - public ChainProcessingContext withError(RuleEngineError error) { - if (error != null && (this.error == null || this.error.getPriority() < error.getPriority())) { - return new ChainProcessingContext(this, 0, error); - } else { - return this; - } - } - - public int getChainLength() { - return md.chain.size(); - } - - public ToDeviceActorMsg getInMsg() { - return md.inMsg; - } - - public DeviceMetaData getDeviceMetaData() { - return md.deviceMetaData; - } - - public String getDeviceName() { - return md.deviceMetaData.getDeviceName(); - } - - public String getDeviceType() { - return md.deviceMetaData.getDeviceType(); - } - - public DeviceAttributes getAttributes() { - return md.deviceMetaData.getDeviceAttributes(); - } - - public ToDeviceMsg getResponse() { - return response; - } - - public void mergeResponse(ToDeviceMsg response) { - // TODO add merge logic - this.response = response; - } - - public RuleEngineErrorMsg getError() { - return new RuleEngineErrorMsg(md.inMsg.getPayload().getMsgType(), error); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java deleted file mode 100644 index 8080d4ecc9..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ChainProcessingMetaData.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import akka.actor.ActorRef; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; - -/** - * Immutable part of chain processing data; - * - * @author ashvayka - */ -public final class ChainProcessingMetaData { - - final RuleActorChain chain; - final ToDeviceActorMsg inMsg; - final ActorRef originator; - final DeviceMetaData deviceMetaData; - - public ChainProcessingMetaData(RuleActorChain chain, ToDeviceActorMsg inMsg, DeviceMetaData deviceMetaData, ActorRef originator) { - super(); - this.chain = chain; - this.inMsg = inMsg; - this.originator = originator; - this.deviceMetaData = deviceMetaData; - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java b/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java deleted file mode 100644 index 89d31bdc84..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/ComplexRuleActorChain.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -public class ComplexRuleActorChain implements RuleActorChain { - - private final RuleActorChain systemChain; - private final RuleActorChain tenantChain; - - public ComplexRuleActorChain(RuleActorChain systemChain, RuleActorChain tenantChain) { - super(); - this.systemChain = systemChain; - this.tenantChain = tenantChain; - } - - @Override - public int size() { - return systemChain.size() + tenantChain.size(); - } - - @Override - public RuleActorMetaData getRuleActorMd(int index) { - if (index < systemChain.size()) { - return systemChain.getRuleActorMd(index); - } else { - return tenantChain.getRuleActorMd(index - systemChain.size()); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java deleted file mode 100644 index 062da2a3bb..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActor.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.ComponentActor; -import org.thingsboard.server.actors.service.ContextBasedCreator; -import org.thingsboard.server.actors.stats.StatsPersistTick; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; - -public class RuleActor extends ComponentActor { - - private RuleActor(ActorSystemContext systemContext, TenantId tenantId, RuleId ruleId) { - super(systemContext, tenantId, ruleId); - setProcessor(new RuleActorMessageProcessor(tenantId, ruleId, systemContext, logger)); - } - - @Override - public void onReceive(Object msg) throws Exception { - logger.debug("[{}] Received message: {}", id, msg); - if (msg instanceof RuleProcessingMsg) { - try { - processor.onRuleProcessingMsg(context(), (RuleProcessingMsg) msg); - increaseMessagesProcessedCount(); - } catch (Exception e) { - logAndPersist("onDeviceMsg", e); - } - } else if (msg instanceof PluginToRuleMsg) { - try { - processor.onPluginMsg(context(), (PluginToRuleMsg) msg); - } catch (Exception e) { - logAndPersist("onPluginMsg", e); - } - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - onClusterEventMsg((ClusterEventMsg) msg); - } else if (msg instanceof RuleToPluginTimeoutMsg) { - try { - processor.onTimeoutMsg(context(), (RuleToPluginTimeoutMsg) msg); - } catch (Exception e) { - logAndPersist("onTimeoutMsg", e); - } - } else if (msg instanceof StatsPersistTick) { - onStatsPersistTick(id); - } else { - logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName()); - } - } - - public static class ActorCreator extends ContextBasedCreator { - private static final long serialVersionUID = 1L; - - private final TenantId tenantId; - private final RuleId ruleId; - - public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleId ruleId) { - super(context); - this.tenantId = tenantId; - this.ruleId = ruleId; - } - - @Override - public RuleActor create() throws Exception { - return new RuleActor(context, tenantId, ruleId); - } - } - - @Override - protected long getErrorPersistFrequency() { - return systemContext.getRuleErrorPersistFrequency(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java deleted file mode 100644 index fd84fe978f..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java +++ /dev/null @@ -1,354 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.event.LoggingAdapter; -import com.fasterxml.jackson.core.JsonProcessingException; -import org.springframework.util.StringUtils; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.plugin.RuleToPluginMsgWrapper; -import org.thingsboard.server.actors.shared.ComponentMsgProcessor; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; -import org.thingsboard.server.common.msg.core.RuleEngineError; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.plugins.PluginAction; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.rules.*; - -import java.util.*; - -class RuleActorMessageProcessor extends ComponentMsgProcessor { - - private final RuleProcessingContext ruleCtx; - private final Map pendingMsgMap; - - private RuleMetaData ruleMd; - private ComponentLifecycleState state; - private List filters; - private RuleProcessor processor; - private PluginAction action; - - private TenantId pluginTenantId; - private PluginId pluginId; - - protected RuleActorMessageProcessor(TenantId tenantId, RuleId ruleId, ActorSystemContext systemContext, LoggingAdapter logger) { - super(systemContext, logger, tenantId, ruleId); - this.pendingMsgMap = new HashMap<>(); - this.ruleCtx = new RuleProcessingContext(systemContext, ruleId); - } - - @Override - public void start() throws Exception { - logger.info("[{}][{}] Starting rule actor.", entityId, tenantId); - ruleMd = systemContext.getRuleService().findRuleById(entityId); - if (ruleMd == null) { - throw new RuleInitializationException("Rule not found!"); - } - state = ruleMd.getState(); - if (state == ComponentLifecycleState.ACTIVE) { - logger.info("[{}] Rule is active. Going to initialize rule components.", entityId); - initComponent(); - } else { - logger.info("[{}] Rule is suspended. Skipping rule components initialization.", entityId); - } - - logger.info("[{}][{}] Started rule actor.", entityId, tenantId); - } - - @Override - public void stop() throws Exception { - onStop(); - } - - - private void initComponent() throws RuleException { - try { - if (!ruleMd.getFilters().isArray()) { - throw new RuntimeException("Filters are not array!"); - } - fetchPluginInfo(); - initFilters(); - initProcessor(); - initAction(); - } catch (RuntimeException e) { - throw new RuleInitializationException("Unknown runtime exception!", e); - } catch (InstantiationException e) { - throw new RuleInitializationException("No default constructor for rule implementation!", e); - } catch (IllegalAccessException e) { - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e); - } catch (ClassNotFoundException e) { - throw new RuleInitializationException("Rule Class not found!", e); - } catch (Exception e) { - throw new RuleException(e.getMessage(), e); - } - } - - private void initAction() throws Exception { - if (ruleMd.getAction() != null && !ruleMd.getAction().isNull()) { - action = initComponent(ruleMd.getAction()); - } - } - - private void initProcessor() throws Exception { - if (ruleMd.getProcessor() != null && !ruleMd.getProcessor().isNull()) { - processor = initComponent(ruleMd.getProcessor()); - } - } - - private void initFilters() throws Exception { - filters = new ArrayList<>(ruleMd.getFilters().size()); - for (int i = 0; i < ruleMd.getFilters().size(); i++) { - filters.add(initComponent(ruleMd.getFilters().get(i))); - } - } - - private void fetchPluginInfo() { - if (!StringUtils.isEmpty(ruleMd.getPluginToken())) { - PluginMetaData pluginMd = systemContext.getPluginService().findPluginByApiToken(ruleMd.getPluginToken()); - pluginTenantId = pluginMd.getTenantId(); - pluginId = pluginMd.getId(); - } - } - - protected void onRuleProcessingMsg(ActorContext context, RuleProcessingMsg msg) throws RuleException { - if (state != ComponentLifecycleState.ACTIVE) { - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_ACTIVE_RULES); - return; - } - ChainProcessingContext chainCtx = msg.getCtx(); - ToDeviceActorMsg inMsg = chainCtx.getInMsg(); - - ruleCtx.update(inMsg, chainCtx.getDeviceMetaData()); - - logger.debug("[{}] Going to filter in msg: {}", entityId, inMsg); - for (RuleFilter filter : filters) { - if (!filter.filter(ruleCtx, inMsg)) { - logger.debug("[{}] In msg is NOT valid for processing by current rule: {}", entityId, inMsg); - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_FILTERS_MATCHED); - return; - } - } - RuleProcessingMetaData inMsgMd; - if (processor != null) { - logger.debug("[{}] Going to process in msg: {}", entityId, inMsg); - inMsgMd = processor.process(ruleCtx, inMsg); - } else { - inMsgMd = new RuleProcessingMetaData(); - } - logger.debug("[{}] Going to convert in msg: {}", entityId, inMsg); - if (action != null) { - Optional> ruleToPluginMsgOptional = action.convert(ruleCtx, inMsg, inMsgMd); - if (ruleToPluginMsgOptional.isPresent()) { - RuleToPluginMsg ruleToPluginMsg = ruleToPluginMsgOptional.get(); - logger.debug("[{}] Device msg is converted to: {}", entityId, ruleToPluginMsg); - context.parent().tell(new RuleToPluginMsgWrapper(pluginTenantId, pluginId, tenantId, entityId, ruleToPluginMsg), context.self()); - if (action.isOneWayAction()) { - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS); - return; - } else { - pendingMsgMap.put(ruleToPluginMsg.getUid(), msg); - scheduleMsgWithDelay(context, new RuleToPluginTimeoutMsg(ruleToPluginMsg.getUid()), systemContext.getPluginProcessingTimeout()); - return; - } - } - } - logger.debug("[{}] Nothing to send to plugin: {}", entityId, pluginId); - pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS); - } - - void onPluginMsg(ActorContext context, PluginToRuleMsg msg) { - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getUid()); - if (pendingMsg != null) { - ChainProcessingContext ctx = pendingMsg.getCtx(); - if (isErrorResponce(msg)) { - pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS); - } else { - Optional ruleResponseOptional = action.convert(msg); - if (ruleResponseOptional.isPresent()) { - ctx.mergeResponse(ruleResponseOptional.get()); - pushToNextRule(context, ctx, null); - } else { - pushToNextRule(context, ctx, RuleEngineError.NO_RESPONSE_FROM_ACTIONS); - } - } - } else { - logger.warning("[{}] Processing timeout detected: [{}]", entityId, msg.getUid()); - } - } - - private boolean isErrorResponce(PluginToRuleMsg msg) { - if (msg instanceof ResponsePluginToRuleMsg) { - if (((ResponsePluginToRuleMsg) msg).getPayload() instanceof BasicStatusCodeResponse) { - BasicStatusCodeResponse responce = (BasicStatusCodeResponse) ((ResponsePluginToRuleMsg) msg).getPayload(); - return !responce.isSuccess(); - } - } - return false; - } - - void onTimeoutMsg(ActorContext context, RuleToPluginTimeoutMsg msg) { - RuleProcessingMsg pendingMsg = pendingMsgMap.remove(msg.getMsgId()); - if (pendingMsg != null) { - logger.debug("[{}] Processing timeout detected [{}]: {}", entityId, msg.getMsgId(), pendingMsg); - ChainProcessingContext ctx = pendingMsg.getCtx(); - pushToNextRule(context, ctx, RuleEngineError.PLUGIN_TIMEOUT); - } - } - - private void pushToNextRule(ActorContext context, ChainProcessingContext ctx, RuleEngineError error) { - if (error != null) { - ctx = ctx.withError(error); - } - if (ctx.isFailure()) { - logger.debug("[{}][{}] Forwarding processing chain to device actor due to failure.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender()); - } else if (!ctx.hasNext()) { - logger.debug("[{}][{}] Forwarding processing chain to device actor due to end of chain.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ctx.getDeviceActor().tell(new RulesProcessedMsg(ctx), ActorRef.noSender()); - } else { - logger.debug("[{}][{}] Forwarding processing chain to next rule actor.", ruleMd.getId(), ctx.getInMsg().getDeviceId()); - ChainProcessingContext nextTask = ctx.getNext(); - nextTask.getCurrentActor().tell(new RuleProcessingMsg(nextTask), context.self()); - } - } - - @Override - public void onCreated(ActorContext context) { - logger.info("[{}] Going to process onCreated rule.", entityId); - } - - @Override - public void onUpdate(ActorContext context) throws RuleException { - RuleMetaData oldRuleMd = ruleMd; - ruleMd = systemContext.getRuleService().findRuleById(entityId); - logger.info("[{}] Rule configuration was updated from {} to {}.", entityId, oldRuleMd, ruleMd); - try { - fetchPluginInfo(); - if (filters == null || !Objects.equals(oldRuleMd.getFilters(), ruleMd.getFilters())) { - logger.info("[{}] Rule filters require restart due to json change from {} to {}.", - entityId, mapper.writeValueAsString(oldRuleMd.getFilters()), mapper.writeValueAsString(ruleMd.getFilters())); - stopFilters(); - initFilters(); - } - if (processor == null || !Objects.equals(oldRuleMd.getProcessor(), ruleMd.getProcessor())) { - logger.info("[{}] Rule processor require restart due to configuration change.", entityId); - stopProcessor(); - initProcessor(); - } - if (action == null || !Objects.equals(oldRuleMd.getAction(), ruleMd.getAction())) { - logger.info("[{}] Rule action require restart due to configuration change.", entityId); - stopAction(); - initAction(); - } - } catch (RuntimeException e) { - throw new RuleInitializationException("Unknown runtime exception!", e); - } catch (InstantiationException e) { - throw new RuleInitializationException("No default constructor for rule implementation!", e); - } catch (IllegalAccessException e) { - throw new RuleInitializationException("Illegal Access Exception during rule initialization!", e); - } catch (ClassNotFoundException e) { - throw new RuleInitializationException("Rule Class not found!", e); - } catch (JsonProcessingException e) { - throw new RuleInitializationException("Rule configuration is invalid!", e); - } catch (Exception e) { - throw new RuleInitializationException(e.getMessage(), e); - } - } - - @Override - public void onActivate(ActorContext context) throws Exception { - logger.info("[{}] Going to process onActivate rule.", entityId); - this.state = ComponentLifecycleState.ACTIVE; - if (filters != null) { - filters.forEach(RuleLifecycleComponent::resume); - if (processor != null) { - processor.resume(); - } else { - initProcessor(); - } - if (action != null) { - action.resume(); - } - logger.info("[{}] Rule resumed.", entityId); - } else { - start(); - } - } - - @Override - public void onSuspend(ActorContext context) { - logger.info("[{}] Going to process onSuspend rule.", entityId); - this.state = ComponentLifecycleState.SUSPENDED; - if (filters != null) { - filters.forEach(f -> f.suspend()); - } - if (processor != null) { - processor.suspend(); - } - if (action != null) { - action.suspend(); - } - } - - @Override - public void onStop(ActorContext context) { - logger.info("[{}] Going to process onStop rule.", entityId); - onStop(); - scheduleMsgWithDelay(context, new RuleTerminationMsg(entityId), systemContext.getRuleActorTerminationDelay()); - } - - private void onStop() { - this.state = ComponentLifecycleState.SUSPENDED; - stopFilters(); - stopProcessor(); - stopAction(); - } - - @Override - public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { - //Do nothing - } - - private void stopAction() { - if (action != null) { - action.stop(); - } - } - - private void stopProcessor() { - if (processor != null) { - processor.stop(); - } - } - - private void stopFilters() { - if (filters != null) { - filters.forEach(f -> f.stop()); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java deleted file mode 100644 index df7adac396..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMetaData.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import java.util.Comparator; - -import org.thingsboard.server.common.data.id.RuleId; - -import akka.actor.ActorRef; - -public class RuleActorMetaData { - - private final RuleId ruleId; - private final boolean systemRule; - private final int weight; - private final ActorRef actorRef; - - public static final Comparator RULE_ACTOR_MD_COMPARATOR = new Comparator() { - - @Override - public int compare(RuleActorMetaData r1, RuleActorMetaData r2) { - if (r1.isSystemRule() && !r2.isSystemRule()) { - return 1; - } else if (!r1.isSystemRule() && r2.isSystemRule()) { - return -1; - } else { - return Integer.compare(r2.getWeight(), r1.getWeight()); - } - } - }; - - public static RuleActorMetaData systemRule(RuleId ruleId, int weight, ActorRef actorRef) { - return new RuleActorMetaData(ruleId, true, weight, actorRef); - } - - public static RuleActorMetaData tenantRule(RuleId ruleId, int weight, ActorRef actorRef) { - return new RuleActorMetaData(ruleId, false, weight, actorRef); - } - - private RuleActorMetaData(RuleId ruleId, boolean systemRule, int weight, ActorRef actorRef) { - super(); - this.ruleId = ruleId; - this.systemRule = systemRule; - this.weight = weight; - this.actorRef = actorRef; - } - - public RuleId getRuleId() { - return ruleId; - } - - public boolean isSystemRule() { - return systemRule; - } - - public int getWeight() { - return weight; - } - - public ActorRef getActorRef() { - return actorRef; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((ruleId == null) ? 0 : ruleId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - RuleActorMetaData other = (RuleActorMetaData) obj; - if (ruleId == null) { - if (other.ruleId != null) - return false; - } else if (!ruleId.equals(other.ruleId)) - return false; - return true; - } - - @Override - public String toString() { - return "RuleActorMetaData [ruleId=" + ruleId + ", systemRule=" + systemRule + ", weight=" + weight + ", actorRef=" + actorRef + "]"; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java deleted file mode 100644 index 179307bb05..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingContext.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.rule; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmId; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; -import org.thingsboard.server.extensions.api.rules.RuleContext; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -public class RuleProcessingContext implements RuleContext { - - private final TimeseriesService tsService; - private final EventService eventService; - private final AlarmService alarmService; - private final RuleId ruleId; - private TenantId tenantId; - private CustomerId customerId; - private DeviceId deviceId; - private DeviceMetaData deviceMetaData; - - RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) { - this.tsService = systemContext.getTsService(); - this.eventService = systemContext.getEventService(); - this.alarmService = systemContext.getAlarmService(); - this.ruleId = ruleId; - } - - void update(ToDeviceActorMsg toDeviceActorMsg, DeviceMetaData deviceMetaData) { - this.tenantId = toDeviceActorMsg.getTenantId(); - this.customerId = toDeviceActorMsg.getCustomerId(); - this.deviceId = toDeviceActorMsg.getDeviceId(); - this.deviceMetaData = deviceMetaData; - } - - @Override - public RuleId getRuleId() { - return ruleId; - } - - @Override - public DeviceMetaData getDeviceMetaData() { - return deviceMetaData; - } - - @Override - public Event save(Event event) { - checkEvent(event); - return eventService.save(event); - } - - @Override - public Optional saveIfNotExists(Event event) { - checkEvent(event); - return eventService.saveIfNotExists(event); - } - - @Override - public Optional findEvent(String eventType, String eventUid) { - return eventService.findEvent(tenantId, deviceId, eventType, eventUid); - } - - @Override - public Alarm createOrUpdateAlarm(Alarm alarm) { - alarm.setTenantId(tenantId); - return alarmService.createOrUpdateAlarm(alarm); - } - - public Optional findLatestAlarm(EntityId originator, String alarmType) { - try { - return Optional.ofNullable(alarmService.findLatestByOriginatorAndType(tenantId, originator, alarmType).get()); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException("Failed to lookup alarm!", e); - } - } - - @Override - public ListenableFuture clearAlarm(AlarmId alarmId, long clearTs) { - return alarmService.clearAlarm(alarmId, clearTs); - } - - private void checkEvent(Event event) { - if (event.getTenantId() == null) { - event.setTenantId(tenantId); - } else if (!tenantId.equals(event.getTenantId())) { - throw new IllegalArgumentException("Invalid Tenant id!"); - } - if (event.getEntityId() == null) { - event.setEntityId(deviceId); - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java new file mode 100644 index 0000000000..bcadd0c8aa --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -0,0 +1,241 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import akka.actor.ActorRef; +import com.datastax.driver.core.utils.UUIDs; +import org.thingsboard.rule.engine.api.ListeningExecutor; +import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse; +import org.thingsboard.rule.engine.api.RuleEngineRpcService; +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; +import scala.concurrent.duration.Duration; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Created by ashvayka on 19.03.18. + */ +class DefaultTbContext implements TbContext { + + private final ActorSystemContext mainCtx; + private final RuleNodeCtx nodeCtx; + + public DefaultTbContext(ActorSystemContext mainCtx, RuleNodeCtx nodeCtx) { + this.mainCtx = mainCtx; + this.nodeCtx = nodeCtx; + } + + @Override + public void tellNext(TbMsg msg, String relationType) { + tellNext(msg, Collections.singleton(relationType), null); + } + + @Override + public void tellNext(TbMsg msg, Set relationTypes) { + tellNext(msg, relationTypes, null); + } + + @Override + public void tellNext(TbMsg msg, String relationType, Throwable th) { + tellNext(msg, Collections.singleton(relationType), th); + } + + private void tellNext(TbMsg msg, Set relationTypes, Throwable th) { + if (nodeCtx.getSelf().isDebugMode()) { + relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); + } + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg), nodeCtx.getSelfActor()); + } + + @Override + public void tellSelf(TbMsg msg, long delayMs) { + //TODO: add persistence layer + scheduleMsgWithDelay(new RuleNodeToSelfMsg(msg), delayMs, nodeCtx.getSelfActor()); + } + + private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { + mainCtx.getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, mainCtx.getActorSystem().dispatcher(), nodeCtx.getSelfActor()); + } + + @Override + public void tellFailure(TbMsg msg, Throwable th) { + if (nodeCtx.getSelf().isDebugMode()) { + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th); + } + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), msg), nodeCtx.getSelfActor()); + } + + @Override + public void updateSelf(RuleNode self) { + nodeCtx.setSelf(self); + } + + @Override + public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), mainCtx.getQueuePartitionId()); + } + + @Override + public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), mainCtx.getQueuePartitionId()); + } + + @Override + public RuleNodeId getSelfId() { + return nodeCtx.getSelf().getId(); + } + + @Override + public TenantId getTenantId() { + return nodeCtx.getTenantId(); + } + + @Override + public ListeningExecutor getJsExecutor() { + return mainCtx.getJsExecutor(); + } + + @Override + public ListeningExecutor getMailExecutor() { + return mainCtx.getMailExecutor(); + } + + @Override + public ListeningExecutor getDbCallbackExecutor() { + return mainCtx.getDbCallbackExecutor(); + } + + @Override + public ListeningExecutor getExternalCallExecutor() { + return mainCtx.getExternalCallExecutorService(); + } + + @Override + public ScriptEngine createJsScriptEngine(String script, String... argNames) { + return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), script, argNames); + } + + @Override + public AttributesService getAttributesService() { + return mainCtx.getAttributesService(); + } + + @Override + public CustomerService getCustomerService() { + return mainCtx.getCustomerService(); + } + + @Override + public UserService getUserService() { + return mainCtx.getUserService(); + } + + @Override + public AssetService getAssetService() { + return mainCtx.getAssetService(); + } + + @Override + public DeviceService getDeviceService() { + return mainCtx.getDeviceService(); + } + + @Override + public AlarmService getAlarmService() { + return mainCtx.getAlarmService(); + } + + @Override + public RuleChainService getRuleChainService() { + return mainCtx.getRuleChainService(); + } + + @Override + public TimeseriesService getTimeseriesService() { + return mainCtx.getTsService(); + } + + @Override + public RuleEngineTelemetryService getTelemetryService() { + return mainCtx.getTsSubService(); + } + + @Override + public RelationService getRelationService() { + return mainCtx.getRelationService(); + } + + @Override + public MailService getMailService() { + if (mainCtx.isAllowSystemMailService()) { + return mainCtx.getMailService(); + } else { + throw new RuntimeException("Access to System Mail Service is forbidden!"); + } + } + + @Override + public RuleEngineRpcService getRpcService() { + return new RuleEngineRpcService() { + @Override + public void sendRpcReply(DeviceId deviceId, int requestId, String body) { + mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body); + } + + @Override + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer consumer) { + ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(), + src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); + mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> { + consumer.accept(RuleEngineDeviceRpcResponse.builder() + .deviceId(src.getDeviceId()) + .requestId(src.getRequestId()) + .error(response.getError()) + .response(response.getResponse()) + .build()); + }); + } + }; + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java new file mode 100644 index 0000000000..3ba646aaf8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import akka.actor.OneForOneStrategy; +import akka.actor.SupervisorStrategy; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; +import org.thingsboard.server.actors.service.ComponentActor; +import org.thingsboard.server.actors.service.ContextBasedCreator; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import scala.concurrent.duration.Duration; + +public class RuleChainActor extends ComponentActor { + + private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) { + super(systemContext, tenantId, ruleChainId); + setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext, + logger, context().parent(), context().self())); + } + + @Override + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG: + processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); + break; + case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: + processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); + break; + case RULE_CHAIN_TO_RULE_CHAIN_MSG: + processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg); + break; + case CLUSTER_EVENT_MSG: + break; + default: + return false; + } + return true; + } + + public static class ActorCreator extends ContextBasedCreator { + private static final long serialVersionUID = 1L; + + private final TenantId tenantId; + private final RuleChainId ruleChainId; + + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) { + super(context); + this.tenantId = tenantId; + this.ruleChainId = pluginId; + } + + @Override + public RuleChainActor create() throws Exception { + return new RuleChainActor(context, tenantId, ruleChainId); + } + } + + @Override + protected long getErrorPersistFrequency() { + return systemContext.getRuleChainErrorPersistFrequency(); + } + + @Override + public SupervisorStrategy supervisorStrategy() { + return strategy; + } + + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> { + logAndPersist("Unknown Failure", ActorSystemContext.toException(t)); + return SupervisorStrategy.resume(); + }); +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java new file mode 100644 index 0000000000..7d560db061 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -0,0 +1,297 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.event.LoggingAdapter; +import com.datastax.driver.core.utils.UUIDs; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; +import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg; +import org.thingsboard.server.actors.service.DefaultActorService; +import org.thingsboard.server.actors.shared.ComponentMsgProcessor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.dao.rule.RuleChainService; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author Andrew Shvayka + */ +public class RuleChainActorMessageProcessor extends ComponentMsgProcessor { + + private static final long DEFAULT_CLUSTER_PARTITION = 0L; + private final ActorRef parent; + private final ActorRef self; + private final Map nodeActors; + private final Map> nodeRoutes; + private final RuleChainService service; + + private RuleNodeId firstId; + private RuleNodeCtx firstNode; + private boolean started; + + RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext + , LoggingAdapter logger, ActorRef parent, ActorRef self) { + super(systemContext, logger, tenantId, ruleChainId); + this.parent = parent; + this.self = self; + this.nodeActors = new HashMap<>(); + this.nodeRoutes = new HashMap<>(); + this.service = systemContext.getRuleChainService(); + } + + @Override + public void start(ActorContext context) throws Exception { + if (!started) { + RuleChain ruleChain = service.findRuleChainById(entityId); + List ruleNodeList = service.getRuleChainNodes(entityId); + // Creating and starting the actors; + for (RuleNode ruleNode : ruleNodeList) { + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } + initRoutes(ruleChain, ruleNodeList); + reprocess(ruleNodeList); + started = true; + } else { + onUpdate(context); + } + } + + private void reprocess(List ruleNodeList) { + for (RuleNode ruleNode : ruleNodeList) { + for (TbMsg tbMsg : queue.findUnprocessed(tenantId, ruleNode.getId().getId(), systemContext.getQueuePartitionId())) { + pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg, ""); + } + } + if (firstNode != null) { + for (TbMsg tbMsg : queue.findUnprocessed(tenantId, entityId.getId(), systemContext.getQueuePartitionId())) { + pushMsgToNode(firstNode, tbMsg, ""); + } + } + } + + @Override + public void onUpdate(ActorContext context) throws Exception { + RuleChain ruleChain = service.findRuleChainById(entityId); + List ruleNodeList = service.getRuleChainNodes(entityId); + + for (RuleNode ruleNode : ruleNodeList) { + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); + if (existing == null) { + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } else { + existing.setSelf(ruleNode); + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + } + } + + Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + removedRules.forEach(ruleNodeId -> { + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); + }); + + initRoutes(ruleChain, ruleNodeList); + reprocess(ruleNodeList); + } + + @Override + public void stop(ActorContext context) throws Exception { + nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(context::stop); + nodeActors.clear(); + nodeRoutes.clear(); + context.stop(self); + started = false; + } + + @Override + public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { + + } + + private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) { + String dispatcherName = tenantId.getId().equals(EntityId.NULL_UUID) ? + DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME : DefaultActorService.TENANT_RULE_DISPATCHER_NAME; + return context.actorOf( + Props.create(new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleNode.getId())) + .withDispatcher(dispatcherName), ruleNode.getId().toString()); + } + + private void initRoutes(RuleChain ruleChain, List ruleNodeList) { + nodeRoutes.clear(); + // Populating the routes map; + for (RuleNode ruleNode : ruleNodeList) { + List relations = service.getRuleNodeRelations(ruleNode.getId()); + if (relations.size() == 0) { + nodeRoutes.put(ruleNode.getId(), Collections.emptyList()); + } else { + for (EntityRelation relation : relations) { + if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { + RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId())); + if (ruleNodeCtx == null) { + throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]"); + } + } + nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>()) + .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType())); + } + } + } + + firstId = ruleChain.getFirstRuleNodeId(); + firstNode = nodeActors.get(ruleChain.getFirstRuleNodeId()); + state = ComponentLifecycleState.ACTIVE; + } + + void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { + checkActive(); + if (firstNode != null) { + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg, "")); + } + } + + void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) { + checkActive(); + if (firstNode != null) { + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> { + pushMsgToNode(firstNode, msg, ""); + envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self); + }); + } + } + + void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) { + checkActive(); + if (envelope.isEnqueue()) { + if (firstNode != null) { + putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg, envelope.getFromRelationType())); + } + } else { + if (firstNode != null) { + pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType()); + } else { + TbMsg msg = envelope.getMsg(); + EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId(); + queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition()); + } + } + } + + void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { + checkActive(); + RuleNodeId originator = envelope.getOriginator(); + List relations = nodeRoutes.get(originator).stream() + .filter(r -> contains(envelope.getRelationTypes(), r.getType())) + .collect(Collectors.toList()); + + TbMsg msg = envelope.getMsg(); + int relationsCount = relations.size(); + EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId(); + if (relationsCount == 0) { + queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition()); + } else if (relationsCount == 1) { + for (RuleNodeRelation relation : relations) { + pushToTarget(msg, relation.getOut(), relation.getType()); + } + } else { + for (RuleNodeRelation relation : relations) { + EntityId target = relation.getOut(); + switch (target.getEntityType()) { + case RULE_NODE: + enqueueAndForwardMsgCopyToNode(msg, target, relation.getType()); + break; + case RULE_CHAIN: + enqueueAndForwardMsgCopyToChain(msg, target, relation.getType()); + break; + } + } + //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues. + queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition()); + } + } + + private boolean contains(Set relationTypes, String type) { + if (relationTypes == null) { + return true; + } + for (String relationType : relationTypes) { + if (relationType.equalsIgnoreCase(type)) { + return true; + } + } + return false; + } + + private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target, String fromRelationType) { + RuleChainId targetRCId = new RuleChainId(target.getId()); + TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION); + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, copyMsg, fromRelationType, true), self); + } + + private void enqueueAndForwardMsgCopyToNode(TbMsg msg, EntityId target, String fromRelationType) { + RuleNodeId targetId = new RuleNodeId(target.getId()); + RuleNodeCtx targetNodeCtx = nodeActors.get(targetId); + TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION); + putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg, fromRelationType)); + } + + private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) { + switch (target.getEntityType()) { + case RULE_NODE: + pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType); + break; + case RULE_CHAIN: + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType, false), self); + break; + } + } + + private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) { + if (nodeCtx != null) { + nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self); + } + } + + private TbMsg enrichWithRuleChainId(TbMsg tbMsg) { + // We don't put firstNodeId because it may change over time; + return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getData(), entityId, null, systemContext.getQueuePartitionId()); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java new file mode 100644 index 0000000000..47ee796141 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import akka.actor.ActorRef; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.shared.rulechain.RuleChainManager; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * Created by ashvayka on 15.03.18. + */ +public abstract class RuleChainManagerActor extends ContextAwareActor { + + protected final RuleChainManager ruleChainManager; + protected final RuleChainService ruleChainService; + + public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager) { + super(systemContext); + this.ruleChainManager = ruleChainManager; + this.ruleChainService = systemContext.getRuleChainService(); + } + + protected void initRuleChains() { + ruleChainManager.init(this.context()); + } + + protected ActorRef getEntityActorRef(EntityId entityId) { + ActorRef target = null; + switch (entityId.getEntityType()) { + case RULE_CHAIN: + target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId); + break; + } + return target; + } + + protected void broadcast(Object msg) { + ruleChainManager.broadcast(msg); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java new file mode 100644 index 0000000000..8b13747fb6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import lombok.Data; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 19.03.18. + */ +@Data +public final class RuleChainToRuleChainMsg implements TbActorMsg { + + private final RuleChainId target; + private final RuleChainId source; + private final TbMsg msg; + private final String fromRelationType; + private final boolean enqueue; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG; + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java new file mode 100644 index 0000000000..abcddc91fc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import lombok.Data; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 19.03.18. + */ +@Data +final class RuleChainToRuleNodeMsg implements TbActorMsg { + + private final TbContext ctx; + private final TbMsg msg; + private final String fromRelationType; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_CHAIN_TO_RULE_MSG; + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java new file mode 100644 index 0000000000..f7ca0d8cc7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java @@ -0,0 +1,109 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.ComponentActor; +import org.thingsboard.server.actors.service.ContextBasedCreator; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; + +public class RuleNodeActor extends ComponentActor { + + private final RuleChainId ruleChainId; + + private RuleNodeActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + super(systemContext, tenantId, ruleNodeId); + this.ruleChainId = ruleChainId; + setProcessor(new RuleNodeActorMessageProcessor(tenantId, ruleChainId, ruleNodeId, systemContext, + logger, context().parent(), context().self())); + } + + @Override + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case RULE_CHAIN_TO_RULE_MSG: + onRuleChainToRuleNodeMsg((RuleChainToRuleNodeMsg) msg); + break; + case RULE_TO_SELF_ERROR_MSG: + onRuleNodeToSelfErrorMsg((RuleNodeToSelfErrorMsg) msg); + break; + case RULE_TO_SELF_MSG: + onRuleNodeToSelfMsg((RuleNodeToSelfMsg) msg); + break; + default: + return false; + } + return true; + } + + private void onRuleNodeToSelfMsg(RuleNodeToSelfMsg msg) { + logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); + try { + processor.onRuleToSelfMsg(msg); + increaseMessagesProcessedCount(); + } catch (Exception e) { + logAndPersist("onRuleMsg", e); + } + } + + private void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) { + logger.debug("[{}] Going to process rule msg: {}", id, msg.getMsg()); + try { + processor.onRuleChainToRuleNodeMsg(msg); + increaseMessagesProcessedCount(); + } catch (Exception e) { + logAndPersist("onRuleMsg", e); + } + } + + private void onRuleNodeToSelfErrorMsg(RuleNodeToSelfErrorMsg msg) { + logAndPersist("onRuleMsg", ActorSystemContext.toException(msg.getError())); + } + + public static class ActorCreator extends ContextBasedCreator { + private static final long serialVersionUID = 1L; + + private final TenantId tenantId; + private final RuleChainId ruleChainId; + private final RuleNodeId ruleNodeId; + + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + super(context); + this.tenantId = tenantId; + this.ruleChainId = ruleChainId; + this.ruleNodeId = ruleNodeId; + + } + + @Override + public RuleNodeActor create() throws Exception { + return new RuleNodeActor(context, tenantId, ruleChainId, ruleNodeId); + } + } + + @Override + protected long getErrorPersistFrequency() { + return systemContext.getRuleNodeErrorPersistFrequency(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java new file mode 100644 index 0000000000..acb171d959 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.event.LoggingAdapter; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.shared.ComponentMsgProcessor; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * @author Andrew Shvayka + */ +public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor { + + private final ActorRef parent; + private final ActorRef self; + private final RuleChainService service; + private RuleNode ruleNode; + private TbNode tbNode; + private TbContext defaultCtx; + + RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext + , LoggingAdapter logger, ActorRef parent, ActorRef self) { + super(systemContext, logger, tenantId, ruleNodeId); + this.parent = parent; + this.self = self; + this.service = systemContext.getRuleChainService(); + this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); + this.defaultCtx = new DefaultTbContext(systemContext, new RuleNodeCtx(tenantId, parent, self, ruleNode)); + } + + @Override + public void start(ActorContext context) throws Exception { + tbNode = initComponent(ruleNode); + state = ComponentLifecycleState.ACTIVE; + } + + @Override + public void onUpdate(ActorContext context) throws Exception { + RuleNode newRuleNode = systemContext.getRuleChainService().findRuleNodeById(entityId); + boolean restartRequired = !(ruleNode.getType().equals(newRuleNode.getType()) + && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration())); + this.ruleNode = newRuleNode; + this.defaultCtx.updateSelf(newRuleNode); + if (restartRequired) { + if (tbNode != null) { + tbNode.destroy(); + } + start(context); + } + } + + @Override + public void stop(ActorContext context) throws Exception { + if (tbNode != null) { + tbNode.destroy(); + } + context.stop(self); + } + + @Override + public void onClusterEventMsg(ClusterEventMsg msg) throws Exception { + + } + + public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception { + checkActive(); + if (ruleNode.isDebugMode()) { + systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self"); + } + try { + tbNode.onMsg(defaultCtx, msg.getMsg()); + } catch (Exception e) { + defaultCtx.tellFailure(msg.getMsg(), e); + } + } + + void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception { + checkActive(); + if (ruleNode.isDebugMode()) { + systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); + } + try { + tbNode.onMsg(msg.getCtx(), msg.getMsg()); + } catch (Exception e) { + msg.getCtx().tellFailure(msg.getMsg(), e); + } + } + + private TbNode initComponent(RuleNode ruleNode) throws Exception { + Class componentClazz = Class.forName(ruleNode.getType()); + TbNode tbNode = (TbNode) (componentClazz.newInstance()); + tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + return tbNode; + } + +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java similarity index 64% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java index 1122bad9f6..10fcc8b606 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginRpcResponseDeviceMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeCtx.java @@ -13,19 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.actors.ruleChain; +import akka.actor.ActorRef; +import lombok.AllArgsConstructor; import lombok.Data; -import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleNode; /** - * @author Andrew Shvayka + * Created by ashvayka on 19.03.18. */ @Data -public class ToPluginRpcResponseDeviceMsg implements ToPluginActorMsg { - private final PluginId pluginId; - private final TenantId pluginTenantId; - private final FromDeviceRpcResponse response; +@AllArgsConstructor +final class RuleNodeCtx { + private final TenantId tenantId; + private final ActorRef chainActor; + private final ActorRef selfActor; + private RuleNode self; } - diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java similarity index 71% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java index a018af198f..7861e5473c 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/MethodNameFilterConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeRelation.java @@ -13,21 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.filter; +package org.thingsboard.server.actors.ruleChain; import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; /** - * @author Andrew Shvayka + * Created by ashvayka on 19.03.18. */ -@Data -public class MethodNameFilterConfiguration { - private MethodName[] methodNames; +@Data +final class RuleNodeRelation { - @Data - public static class MethodName { - private String name; - } + private final EntityId in; + private final EntityId out; + private final String type; } diff --git a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java similarity index 52% rename from extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java rename to application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java index d98d3dcabb..c0a475c953 100644 --- a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/plugin/RabbitMqPluginConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToRuleChainTellNextMsg.java @@ -13,35 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.rabbitmq.plugin; +package org.thingsboard.server.actors.ruleChain; import lombok.Data; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; -import java.util.List; +import java.util.Set; /** - * @author Andrew Shvayka + * Created by ashvayka on 19.03.18. */ @Data -public class RabbitMqPluginConfiguration { - private String host; - private int port; - private String virtualHost; +final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg { - private String userName; - private String password; + private final RuleNodeId originator; + private final Set relationTypes; + private final TbMsg msg; - private Boolean automaticRecoveryEnabled; - - private Integer connectionTimeout; - private Integer handshakeTimeout; - - private List clientProperties; - - @Data - public static class RabbitMqPluginProperties { - private String key; - private String value; + @Override + public MsgType getMsgType() { + return MsgType.RULE_TO_RULE_CHAIN_TELL_NEXT_MSG; } } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java new file mode 100644 index 0000000000..e6248f199f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfErrorMsg.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 19.03.18. + */ +@Data +final class RuleNodeToSelfErrorMsg implements TbActorMsg { + + private final TbMsg msg; + private final Throwable error; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_TO_SELF_ERROR_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java new file mode 100644 index 0000000000..d1bac4d7c5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeToSelfMsg.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2018 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.actors.ruleChain; + +import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 19.03.18. + */ +@Data +final class RuleNodeToSelfMsg implements TbActorMsg { + + private final TbMsg msg; + + @Override + public MsgType getMsgType() { + return MsgType.RULE_TO_SELF_MSG; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java index baae376c64..f7e80e4baa 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java @@ -16,21 +16,24 @@ package org.thingsboard.server.actors.service; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.common.transport.SessionMsgProcessor; import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener; import org.thingsboard.server.service.cluster.rpc.RpcMsgListener; -public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor, RestMsgProcessor, RpcMsgListener, DiscoveryServiceListener { +public interface ActorService extends SessionMsgProcessor, RpcMsgListener, DiscoveryServiceListener { - void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state); + void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state); - void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state); + void onMsg(SendToClusterMsg msg); void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId); void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); + + void onMsg(ServiceToRuleEngineMsg serviceToRuleEngineMsg); } diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java index 76b9be96b3..6aa68d3c5b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/ComponentActor.java @@ -54,7 +54,7 @@ public abstract class ComponentActor implements Creator { 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 bb84a30229..3507f244e9 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 @@ -19,35 +19,32 @@ import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.actor.Terminated; +import com.google.protobuf.ByteString; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.app.AppActor; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcManagerActor; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; -import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; import org.thingsboard.server.actors.session.SessionManagerActor; import org.thingsboard.server.actors.stats.StatsActor; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; -import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.discovery.ServerInstance; import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; @@ -57,7 +54,8 @@ import scala.concurrent.duration.Duration; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.Optional; + +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE; @Service @Slf4j @@ -93,7 +91,7 @@ public class DefaultActorService implements ActorService { @PostConstruct public void initActorSystem() { - log.info("Initializing Actor system. {}", actorContext.getRuleService()); + log.info("Initializing Actor system. {}", actorContext.getRuleChainService()); actorContext.setActorService(this); system = ActorSystem.create(ACTOR_SYSTEM_NAME, actorContext.getConfig()); actorContext.setActorSystem(system); @@ -129,71 +127,16 @@ public class DefaultActorService implements ActorService { } @Override - public void process(SessionAwareMsg msg) { - log.debug("Processing session aware msg: {}", msg); - sessionManagerActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void process(PluginWebsocketMsg msg) { - log.debug("Processing websocket msg: {}", msg); - appActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void process(PluginRestMsg msg) { - log.debug("Processing rest msg: {}", msg); - appActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(ToPluginActorMsg msg) { - log.trace("Processing plugin rpc msg: {}", msg); - appActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(ToDeviceActorMsg msg) { - log.trace("Processing device rpc msg: {}", msg); + public void onMsg(SendToClusterMsg msg) { appActor.tell(msg, ActorRef.noSender()); } @Override - public void onMsg(ToDeviceActorNotificationMsg msg) { - log.trace("Processing notification rpc msg: {}", msg); - appActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(ToDeviceSessionActorMsg msg) { - log.trace("Processing session rpc msg: {}", msg); + public void process(SessionAwareMsg msg) { + log.debug("Processing session aware msg: {}", msg); sessionManagerActor.tell(msg, ActorRef.noSender()); } - @Override - public void onMsg(ToAllNodesMsg msg) { - log.trace("Processing broadcast rpc msg: {}", msg); - appActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(RpcSessionCreateRequestMsg msg) { - log.trace("Processing session create msg: {}", msg); - rpcManagerActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(RpcSessionTellMsg msg) { - log.trace("Processing session rpc msg: {}", msg); - rpcManagerActor.tell(msg, ActorRef.noSender()); - } - - @Override - public void onMsg(RpcBroadcastMsg msg) { - log.trace("Processing broadcast rpc msg: {}", msg); - rpcManagerActor.tell(msg, ActorRef.noSender()); - } - @Override public void onServerAdded(ServerInstance server) { log.trace("Processing onServerAdded msg: {}", server); @@ -212,42 +155,37 @@ public class DefaultActorService implements ActorService { } @Override - public void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state) { - log.trace("[{}] Processing onPluginStateChange event: {}", pluginId, state); - broadcast(ComponentLifecycleMsg.forPlugin(tenantId, pluginId, state)); - } - - @Override - public void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state) { - log.trace("[{}] Processing onRuleStateChange event: {}", ruleId, state); - broadcast(ComponentLifecycleMsg.forRule(tenantId, ruleId, state)); + public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) { + log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state); + broadcast(new ComponentLifecycleMsg(tenantId, entityId, state)); } @Override public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) { DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId); - Optional address = actorContext.getRoutingService().resolveById(deviceId); - if (address.isPresent()) { - rpcService.tell(address.get(), msg); - } else { - onMsg(msg); - } + appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender()); } @Override public void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType) { log.trace("[{}] Processing onDeviceNameOrTypeUpdate event, deviceName: {}, deviceType: {}", deviceId, deviceName, deviceType); DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType); - Optional address = actorContext.getRoutingService().resolveById(deviceId); - if (address.isPresent()) { - rpcService.tell(address.get(), msg); - } else { - onMsg(msg); - } + appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender()); + } + + @Override + public void onMsg(ServiceToRuleEngineMsg msg) { + appActor.tell(msg, ActorRef.noSender()); } public void broadcast(ToAllNodesMsg msg) { - rpcService.broadcast(msg); + actorContext.getEncodingService().encode(msg); + rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage + .newBuilder() + .setPayload(ByteString + .copyFrom(actorContext.getEncodingService().encode(msg))) + .setMessageType(CLUSTER_ACTOR_MESSAGE) + .build())); appActor.tell(msg, ActorRef.noSender()); } @@ -256,4 +194,64 @@ public class DefaultActorService implements ActorService { this.sessionManagerActor.tell(msg, ActorRef.noSender()); this.rpcManagerActor.tell(msg, ActorRef.noSender()); } + + @Override + public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) { + ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort()); + log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress); + if(log.isDebugEnabled()){ + log.info("MSG: ", msg); + } + switch (msg.getMessageType()) { + case CLUSTER_ACTOR_MESSAGE: + java.util.Optional decodedMsg = actorContext.getEncodingService() + .decode(msg.getPayload().toByteArray()); + if (decodedMsg.isPresent()) { + appActor.tell(decodedMsg.get(), ActorRef.noSender()); + } else { + log.error("Error during decoding cluster proto message"); + } + break; + case TO_ALL_NODES_MSG: + //TODO + break; + case CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE: + actorContext.getTsSubService().onNewRemoteSubscription(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE: + actorContext.getTsSubService().onRemoteSubscriptionUpdate(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE: + actorContext.getTsSubService().onRemoteSubscriptionClose(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE: + actorContext.getTsSubService().onRemoteSessionClose(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE: + actorContext.getTsSubService().onRemoteAttributesUpdate(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE: + actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray()); + break; + case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE: + actorContext.getDeviceRpcService().processRemoteResponseFromDevice(serverAddress, msg.getPayload().toByteArray()); + break; + } + } + + @Override + public void onSendMsg(ClusterAPIProtos.ClusterMessage msg) { + rpcManagerActor.tell(msg, ActorRef.noSender()); + } + + @Override + public void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg) { + rpcManagerActor.tell(msg, ActorRef.noSender()); + } + + @Override + public void onBroadcastMsg(RpcBroadcastMsg msg) { + rpcManagerActor.tell(msg, ActorRef.noSender()); + } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java deleted file mode 100644 index d0c9ca6202..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/service/WebSocketMsgProcessor.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.service; - -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; - -public interface WebSocketMsgProcessor { - - void process(PluginWebsocketMsg msg); - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java index ab75cdb9fe..a8f14fedf5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/session/ASyncMsgProcessor.java @@ -15,19 +15,26 @@ */ package org.thingsboard.server.actors.session; +import akka.actor.ActorContext; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.core.*; +import org.thingsboard.server.common.msg.core.AttributesSubscribeMsg; +import org.thingsboard.server.common.msg.core.ResponseMsg; +import org.thingsboard.server.common.msg.core.RpcSubscribeMsg; import org.thingsboard.server.common.msg.core.SessionCloseMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.*; - -import akka.actor.ActorContext; -import akka.event.LoggingAdapter; -import org.thingsboard.server.common.msg.session.ctrl.*; +import org.thingsboard.server.common.msg.core.SessionOpenMsg; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; +import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg; +import org.thingsboard.server.common.msg.session.FromDeviceMsg; +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionType; +import org.thingsboard.server.common.msg.session.ToDeviceMsg; +import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.ex.SessionException; import java.util.HashMap; @@ -37,7 +44,7 @@ import java.util.Optional; class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { private boolean firstMsg = true; - private Map pendingMap = new HashMap<>(); + private Map pendingMap = new HashMap<>(); private Optional currentTargetServer; private boolean subscribedToAttributeUpdates; private boolean subscribedToRpcCommands; @@ -47,14 +54,16 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { } @Override - protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) { + protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) { updateSessionCtx(msg, SessionType.ASYNC); + DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg); + FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload(); if (firstMsg) { - toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); + if (fromDeviceMsg.getMsgType() != SessionMsgType.SESSION_OPEN) { + toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); + } firstMsg = false; } - ToDeviceActorMsg pendingMsg = toDeviceMsg(msg); - FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload(); switch (fromDeviceMsg.getMsgType()) { case POST_TELEMETRY_REQUEST: case POST_ATTRIBUTES_REQUEST: @@ -86,8 +95,8 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { @Override public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) { try { - if (msg.getMsgType() != MsgType.SESSION_CLOSE) { - switch (msg.getMsgType()) { + if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) { + switch (msg.getSessionMsgType()) { case STATUS_CODE_RESPONSE: case GET_ATTRIBUTES_RESPONSE: ResponseMsg responseMsg = (ResponseMsg) msg; diff --git a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java index 96526dda88..469cda9c5f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/session/AbstractSessionActorMsgProcessor.java @@ -15,36 +15,42 @@ */ package org.thingsboard.server.actors.session; +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.device.BasicToDeviceActorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.*; +import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; +import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; +import org.thingsboard.server.common.msg.session.FromDeviceMsg; +import org.thingsboard.server.common.msg.session.SessionContext; +import org.thingsboard.server.common.msg.session.SessionCtrlMsg; +import org.thingsboard.server.common.msg.session.SessionType; +import org.thingsboard.server.common.msg.session.ToDeviceMsg; +import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.event.LoggingAdapter; - import java.util.Optional; abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgProcessor { protected final SessionId sessionId; protected SessionContext sessionCtx; - protected ToDeviceActorMsg toDeviceActorMsgPrototype; + protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype; protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { super(ctx, logger); this.sessionId = sessionId; } - protected abstract void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg); + protected abstract void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg); protected abstract void processTimeoutMsg(ActorContext context, SessionTimeoutMsg msg); @@ -62,47 +68,44 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP protected void cleanupSession(ActorContext ctx) { } - protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) { + protected void updateSessionCtx(TransportToDeviceSessionActorMsg msg, SessionType type) { sessionCtx = msg.getSessionMsg().getSessionContext(); - toDeviceActorMsgPrototype = new BasicToDeviceActorMsg(msg, type); + deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type); } - protected ToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) { + protected DeviceToDeviceActorMsg toDeviceMsg(TransportToDeviceSessionActorMsg msg) { AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg(); - return new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, adaptorMsg.getMsg()); + return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg()); } - protected Optional toDeviceMsg(FromDeviceMsg msg) { - if (toDeviceActorMsgPrototype != null) { - return Optional.of(new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, msg)); + protected Optional toDeviceMsg(FromDeviceMsg msg) { + if (deviceToDeviceActorMsgPrototype != null) { + return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg)); } else { return Optional.empty(); } } - protected Optional forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) { + protected Optional forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) { Optional address = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); forwardToAppActor(ctx, toForward, address); return address; } - protected Optional forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional oldAddress) { + protected Optional forwardToAppActorIfAddressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional oldAddress) { + Optional newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); if (!newAddress.equals(oldAddress)) { - if (newAddress.isPresent()) { - systemContext.getRpcService().tell(newAddress.get(), - toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())); - } else { - getAppActor().tell(toForward, ctx.self()); - } + getAppActor().tell(new SendToClusterMsg(toForward.getDeviceId(), toForward + .toOtherAddress(systemContext.getRoutingService().getCurrentServer())), ctx.self()); } return newAddress; } - protected void forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward, Optional address) { + protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional address) { if (address.isPresent()) { - systemContext.getRpcService().tell(address.get(), - toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())); + systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(address.get(), + toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()))); } else { getAppActor().tell(toForward, ctx.self()); } @@ -114,6 +117,6 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP } public DeviceId getDeviceId() { - return toDeviceActorMsgPrototype.getDeviceId(); + return deviceToDeviceActorMsgPrototype.getDeviceId(); } } diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java index 37827d61fb..05926c1825 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/session/SessionActor.java @@ -17,22 +17,21 @@ package org.thingsboard.server.actors.session; import akka.actor.OneForOneStrategy; import akka.actor.SupervisorStrategy; -import akka.japi.Function; +import akka.event.Logging; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; +import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; import org.thingsboard.server.common.msg.session.SessionMsg; import org.thingsboard.server.common.msg.session.SessionType; +import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; - -import akka.event.Logging; -import akka.event.LoggingAdapter; import scala.concurrent.duration.Duration; public class SessionActor extends ContextAwareActor { @@ -61,33 +60,38 @@ public class SessionActor extends ContextAwareActor { } @Override - public void onReceive(Object msg) throws Exception { - logger.debug("[{}] Processing: {}.", sessionId, msg); - if (msg instanceof ToDeviceActorSessionMsg) { - processDeviceMsg((ToDeviceActorSessionMsg) msg); - } else if (msg instanceof ToDeviceSessionActorMsg) { - processToDeviceMsg((ToDeviceSessionActorMsg) msg); - } else if (msg instanceof SessionTimeoutMsg) { - processTimeoutMsg((SessionTimeoutMsg) msg); - } else if (msg instanceof SessionCtrlMsg) { - processSessionCtrlMsg((SessionCtrlMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - processClusterEvent((ClusterEventMsg) msg); - } else { - logger.warning("[{}] Unknown msg: {}", sessionId, msg); + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG: + processTransportToSessionMsg((TransportToDeviceSessionActorMsg) msg); + break; + case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: + processActorsToSessionMsg((ActorSystemToDeviceSessionActorMsg) msg); + break; + case SESSION_TIMEOUT_MSG: + processTimeoutMsg((SessionTimeoutMsg) msg); + break; + case SESSION_CTRL_MSG: + processSessionCloseMsg((SessionCtrlMsg) msg); + break; + case CLUSTER_EVENT_MSG: + processClusterEvent((ClusterEventMsg) msg); + break; + default: return false; } + return true; } private void processClusterEvent(ClusterEventMsg msg) { processor.processClusterEvent(context(), msg); } - private void processDeviceMsg(ToDeviceActorSessionMsg msg) { + private void processTransportToSessionMsg(TransportToDeviceSessionActorMsg msg) { initProcessor(msg); processor.processToDeviceActorMsg(context(), msg); } - private void processToDeviceMsg(ToDeviceSessionActorMsg msg) { + private void processActorsToSessionMsg(ActorSystemToDeviceSessionActorMsg msg) { processor.processToDeviceMsg(context(), msg.getMsg()); } @@ -99,7 +103,7 @@ public class SessionActor extends ContextAwareActor { } } - private void processSessionCtrlMsg(SessionCtrlMsg msg) { + private void processSessionCloseMsg(SessionCtrlMsg msg) { if (processor != null) { processor.processSessionCtrlMsg(context(), msg); } else if (msg instanceof SessionCloseMsg) { @@ -109,7 +113,7 @@ public class SessionActor extends ContextAwareActor { } } - private void initProcessor(ToDeviceActorSessionMsg msg) { + private void initProcessor(TransportToDeviceSessionActorMsg msg) { if (processor == null) { SessionMsg sessionMsg = (SessionMsg) msg.getSessionMsg(); if (sessionMsg.getSessionContext().getSessionType() == SessionType.SYNC) { diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java index 9d67dabeb8..624a9f49c7 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/session/SessionManagerActor.java @@ -15,26 +15,29 @@ */ package org.thingsboard.server.actors.session; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import akka.actor.*; +import akka.actor.ActorRef; +import akka.actor.InvalidActorNameException; +import akka.actor.LocalActorRef; +import akka.actor.Props; +import akka.actor.Terminated; +import akka.event.Logging; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg; - -import akka.event.Logging; -import akka.event.LoggingAdapter; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.common.msg.core.ActorSystemToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.core.SessionCloseMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; +import java.util.HashMap; +import java.util.Map; + public class SessionManagerActor extends ContextAwareActor { private static final int INITIAL_SESSION_MAP_SIZE = 1024; @@ -48,6 +51,12 @@ public class SessionManagerActor extends ContextAwareActor { this.sessionActors = new HashMap<>(INITIAL_SESSION_MAP_SIZE); } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg + return false; + } + @Override public void onReceive(Object msg) throws Exception { if (msg instanceof SessionCtrlMsg) { @@ -97,7 +106,7 @@ public class SessionManagerActor extends ContextAwareActor { } private void forwardToSessionActor(SessionAwareMsg msg) { - if (msg instanceof ToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) { + if (msg instanceof ActorSystemToDeviceSessionActorMsg || msg instanceof SessionCloseMsg) { String sessionIdStr = msg.getSessionId().toUidStr(); ActorRef sessionActor = sessionActors.get(sessionIdStr); if (sessionActor != null) { diff --git a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java index d69650347b..cf8df1385f 100644 --- a/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/session/SyncMsgProcessor.java @@ -15,24 +15,26 @@ */ package org.thingsboard.server.actors.session; +import akka.actor.ActorContext; +import akka.event.LoggingAdapter; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.shared.SessionTimeoutMsg; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg; +import org.thingsboard.server.common.msg.session.BasicSessionActorToAdaptorMsg; +import org.thingsboard.server.common.msg.session.SessionContext; +import org.thingsboard.server.common.msg.session.SessionType; +import org.thingsboard.server.common.msg.session.ToDeviceMsg; +import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; import org.thingsboard.server.common.msg.session.ex.SessionException; -import akka.actor.ActorContext; -import akka.event.LoggingAdapter; - import java.util.Optional; class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { - private ToDeviceActorMsg pendingMsg; + private DeviceToDeviceActorMsg pendingMsg; private Optional currentTargetServer; private boolean pendingResponse; @@ -41,7 +43,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { } @Override - protected void processToDeviceActorMsg(ActorContext ctx, ToDeviceActorSessionMsg msg) { + protected void processToDeviceActorMsg(ActorContext ctx, TransportToDeviceSessionActorMsg msg) { updateSessionCtx(msg, SessionType.SYNC); pendingMsg = toDeviceMsg(msg); pendingResponse = true; @@ -73,7 +75,7 @@ class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { @Override public void processClusterEvent(ActorContext context, ClusterEventMsg msg) { if (pendingResponse) { - Optional newTargetServer = forwardToAppActorIfAdressChanged(context, pendingMsg, currentTargetServer); + Optional newTargetServer = forwardToAppActorIfAddressChanged(context, pendingMsg, currentTargetServer); if (logger.isDebugEnabled()) { if (!newTargetServer.equals(currentTargetServer)) { if (newTargetServer.isPresent()) { diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java index 73b221ffc5..8864486150 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/AbstractContextAwareMsgProcessor.java @@ -19,18 +19,13 @@ import akka.actor.ActorContext; import akka.actor.ActorRef; import akka.actor.Scheduler; import akka.event.LoggingAdapter; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Data; import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.common.data.plugin.ComponentDescriptor; -import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.extensions.api.component.*; import scala.concurrent.ExecutionContextExecutor; import scala.concurrent.duration.Duration; -import java.io.IOException; import java.util.concurrent.TimeUnit; public abstract class AbstractContextAwareMsgProcessor { @@ -76,53 +71,6 @@ public abstract class AbstractContextAwareMsgProcessor { getScheduler().scheduleOnce(Duration.create(delayInMs, TimeUnit.MILLISECONDS), target, msg, getSystemDispatcher(), null); } - protected T initComponent(JsonNode componentNode) throws Exception { - ComponentConfiguration configuration = new ComponentConfiguration( - componentNode.get("clazz").asText(), - componentNode.get("name").asText(), - mapper.writeValueAsString(componentNode.get("configuration")) - ); - logger.info("Initializing [{}][{}] component", configuration.getName(), configuration.getClazz()); - ComponentDescriptor componentDescriptor = systemContext.getComponentService().getComponent(configuration.getClazz()) - .orElseThrow(() -> new InstantiationException("Component Not found!")); - return initComponent(componentDescriptor, configuration); - } - - protected T initComponent(ComponentDescriptor componentDefinition, ComponentConfiguration configuration) - throws Exception { - return initComponent(componentDefinition.getClazz(), componentDefinition.getType(), configuration.getConfiguration()); - } - - protected T initComponent(String clazz, ComponentType type, String configuration) - throws Exception { - Class componentClazz = Class.forName(clazz); - T component = (T) (componentClazz.newInstance()); - Class configurationClazz; - switch (type) { - case FILTER: - configurationClazz = ((Filter) componentClazz.getAnnotation(Filter.class)).configuration(); - break; - case PROCESSOR: - configurationClazz = ((Processor) componentClazz.getAnnotation(Processor.class)).configuration(); - break; - case ACTION: - configurationClazz = ((Action) componentClazz.getAnnotation(Action.class)).configuration(); - break; - case PLUGIN: - configurationClazz = ((Plugin) componentClazz.getAnnotation(Plugin.class)).configuration(); - break; - default: - throw new IllegalStateException("Component with type: " + type + " is not supported!"); - } - component.init(decode(configuration, configurationClazz)); - return component; - } - - public C decode(String configuration, Class configurationClazz) throws IOException, RuntimeException { - logger.info("Initializing using configuration: {}", configuration); - return mapper.readValue(configuration, configurationClazz); - } - @Data @AllArgsConstructor private static class ComponentConfiguration { diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java index 18d32d9476..1cf83397fc 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java @@ -17,39 +17,87 @@ package org.thingsboard.server.actors.shared; import akka.actor.ActorContext; import akka.event.LoggingAdapter; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.stats.StatsPersistTick; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; +import org.thingsboard.server.service.queue.MsgQueueService; -public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor { +import javax.annotation.Nullable; +import java.util.function.Consumer; + +public abstract class ComponentMsgProcessor extends AbstractContextAwareMsgProcessor { protected final TenantId tenantId; protected final T entityId; + protected final MsgQueueService queue; + protected ComponentLifecycleState state; protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) { super(systemContext, logger); this.tenantId = tenantId; this.entityId = id; + this.queue = systemContext.getMsgQueueService(); } - public abstract void start() throws Exception; + public abstract void start(ActorContext context) throws Exception; - public abstract void stop() throws Exception; + public abstract void stop(ActorContext context) throws Exception; - public abstract void onCreated(ActorContext context) throws Exception; + public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception; - public abstract void onUpdate(ActorContext context) throws Exception; + public void onCreated(ActorContext context) throws Exception { + start(context); + } - public abstract void onActivate(ActorContext context) throws Exception; + public void onUpdate(ActorContext context) throws Exception { + restart(context); + } - public abstract void onSuspend(ActorContext context) throws Exception; + public void onActivate(ActorContext context) throws Exception { + restart(context); + } - public abstract void onStop(ActorContext context) throws Exception; + public void onSuspend(ActorContext context) throws Exception { + stop(context); + } - public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception; + public void onStop(ActorContext context) throws Exception { + stop(context); + } + + private void restart(ActorContext context) throws Exception { + stop(context); + start(context); + } public void scheduleStatsPersistTick(ActorContext context, long statsPersistFrequency) { schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency); } + + protected void checkActive() { + if (state != ComponentLifecycleState.ACTIVE) { + throw new IllegalStateException("Rule chain is not active!"); + } + } + + protected void putToQueue(final TbMsg tbMsg, final Consumer onSuccess) { + EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId(); + Futures.addCallback(queue.put(this.tenantId, tbMsg, entityId.getId(), tbMsg.getClusterPartition()), new FutureCallback() { + @Override + public void onSuccess(@Nullable Void result) { + onSuccess.accept(tbMsg); + } + + @Override + public void onFailure(Throwable t) { + logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t); + } + }); + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java new file mode 100644 index 0000000000..1b9e6a8074 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2018 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.actors.shared; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.UntypedActor; +import akka.japi.Creator; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.page.PageDataIterable; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by ashvayka on 15.03.18. + */ +@Slf4j +public abstract class EntityActorsManager> { + + protected final ActorSystemContext systemContext; + protected final Map actors; + + public EntityActorsManager(ActorSystemContext systemContext) { + this.systemContext = systemContext; + this.actors = new HashMap<>(); + } + + protected abstract TenantId getTenantId(); + + protected abstract String getDispatcherName(); + + protected abstract Creator creator(T entityId); + + protected abstract PageDataIterable.FetchFunction getFetchEntitiesFunction(); + + public void init(ActorContext context) { + for (M entity : new PageDataIterable<>(getFetchEntitiesFunction(), ContextAwareActor.ENTITY_PACK_LIMIT)) { + T entityId = (T) entity.getId(); + log.debug("[{}|{}] Creating entity actor", entityId.getEntityType(), entityId.getId()); + //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa. + ActorRef actorRef = getOrCreateActor(context, entityId); + visit(entity, actorRef); + log.debug("[{}|{}] Entity actor created.", entityId.getEntityType(), entityId.getId()); + } + } + + public void visit(M entity, ActorRef actorRef) {} + + public ActorRef getOrCreateActor(ActorContext context, T entityId) { + return actors.computeIfAbsent(entityId, eId -> + context.actorOf(Props.create(creator(eId)) + .withDispatcher(getDispatcherName()), eId.toString())); + } + + public void broadcast(Object msg) { + actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); + } + + public void remove(T id) { + actors.remove(id); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java index 7d6dbca6cd..d015fe01de 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/SessionTimeoutMsg.java @@ -17,13 +17,20 @@ package org.thingsboard.server.actors.shared; import lombok.Data; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; import java.io.Serializable; @Data -public class SessionTimeoutMsg implements Serializable { +public class SessionTimeoutMsg implements Serializable, TbActorMsg { private static final long serialVersionUID = 1L; private final SessionId sessionId; + + @Override + public MsgType getMsgType() { + return MsgType.SESSION_TIMEOUT_MSG; + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java deleted file mode 100644 index 4f5871f585..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.shared.plugin; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.plugin.PluginActor; -import org.thingsboard.server.actors.service.ContextAwareActor; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.plugin.PluginService; - -import java.util.HashMap; -import java.util.Map; - -@Slf4j -public abstract class PluginManager { - - protected final ActorSystemContext systemContext; - protected final PluginService pluginService; - protected final Map pluginActors; - - public PluginManager(ActorSystemContext systemContext) { - this.systemContext = systemContext; - this.pluginService = systemContext.getPluginService(); - this.pluginActors = new HashMap<>(); - } - - public void init(ActorContext context) { - PageDataIterable pluginIterator = new PageDataIterable<>(getFetchPluginsFunction(), - ContextAwareActor.ENTITY_PACK_LIMIT); - for (PluginMetaData plugin : pluginIterator) { - log.debug("[{}] Creating plugin actor", plugin.getId()); - getOrCreatePluginActor(context, plugin.getId()); - log.debug("Plugin actor created."); - } - } - - abstract FetchFunction getFetchPluginsFunction(); - - abstract TenantId getTenantId(); - - abstract String getDispatcherName(); - - public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) { - return pluginActors.computeIfAbsent(pluginId, pId -> - context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId)) - .withDispatcher(getDispatcherName()), pId.toString())); - } - - public void broadcast(Object msg) { - pluginActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); - } - - public void remove(PluginId id) { - pluginActors.remove(id); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java deleted file mode 100644 index 0888e23f25..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.shared.plugin; - -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.plugin.BasePluginService; - -public class SystemPluginManager extends PluginManager { - - public SystemPluginManager(ActorSystemContext systemContext) { - super(systemContext); - } - - @Override - FetchFunction getFetchPluginsFunction() { - return pluginService::findSystemPlugins; - } - - @Override - TenantId getTenantId() { - return BasePluginService.SYSTEM_TENANT; - } - - @Override - protected String getDispatcherName() { - return DefaultActorService.SYSTEM_PLUGIN_DISPATCHER_NAME; - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java deleted file mode 100644 index 95d762afdf..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/RuleManager.java +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.shared.rule; - -import akka.actor.ActorContext; -import akka.actor.ActorRef; -import akka.actor.Props; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.rule.RuleActor; -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.actors.rule.RuleActorMetaData; -import org.thingsboard.server.actors.rule.SimpleRuleActorChain; -import org.thingsboard.server.actors.service.ContextAwareActor; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.rule.RuleService; - -import java.util.*; - -@Slf4j -public abstract class RuleManager { - - protected final ActorSystemContext systemContext; - protected final RuleService ruleService; - protected final Map ruleActors; - protected final TenantId tenantId; - - private Map ruleMap; - private RuleActorChain ruleChain; - - public RuleManager(ActorSystemContext systemContext, TenantId tenantId) { - this.systemContext = systemContext; - this.ruleService = systemContext.getRuleService(); - this.ruleActors = new HashMap<>(); - this.tenantId = tenantId; - } - - public void init(ActorContext context) { - doInit(context); - } - - private void doInit(ActorContext context) { - PageDataIterable ruleIterator = new PageDataIterable<>(getFetchRulesFunction(), - ContextAwareActor.ENTITY_PACK_LIMIT); - ruleMap = new HashMap<>(); - - for (RuleMetaData rule : ruleIterator) { - log.debug("[{}] Creating rule actor {}", rule.getId(), rule); - ActorRef ref = getOrCreateRuleActor(context, rule.getId()); - ruleMap.put(rule, RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref)); - log.debug("[{}] Rule actor created.", rule.getId()); - } - - refreshRuleChain(); - } - - public Optional update(ActorContext context, RuleId ruleId, ComponentLifecycleEvent event) { - if (ruleMap == null) { - doInit(context); - } - RuleMetaData rule; - if (event != ComponentLifecycleEvent.DELETED) { - rule = systemContext.getRuleService().findRuleById(ruleId); - } else { - rule = ruleMap.keySet().stream() - .filter(r -> r.getId().equals(ruleId)) - .peek(r -> r.setState(ComponentLifecycleState.SUSPENDED)) - .findFirst() - .orElse(null); - if (rule != null) { - ruleMap.remove(rule); - ruleActors.remove(ruleId); - } - } - if (rule != null) { - RuleActorMetaData actorMd = ruleMap.get(rule); - if (actorMd == null) { - ActorRef ref = getOrCreateRuleActor(context, rule.getId()); - actorMd = RuleActorMetaData.systemRule(rule.getId(), rule.getWeight(), ref); - ruleMap.put(rule, actorMd); - } - refreshRuleChain(); - return Optional.of(actorMd.getActorRef()); - } else { - log.warn("[{}] Can't process unknown rule!", ruleId); - return Optional.empty(); - } - } - - abstract FetchFunction getFetchRulesFunction(); - - abstract String getDispatcherName(); - - public ActorRef getOrCreateRuleActor(ActorContext context, RuleId ruleId) { - return ruleActors.computeIfAbsent(ruleId, rId -> - context.actorOf(Props.create(new RuleActor.ActorCreator(systemContext, tenantId, rId)) - .withDispatcher(getDispatcherName()), rId.toString())); - } - - public RuleActorChain getRuleChain(ActorContext context) { - if (ruleChain == null) { - doInit(context); - } - return ruleChain; - } - - private void refreshRuleChain() { - Set activeRuleSet = new HashSet<>(); - for (Map.Entry rule : ruleMap.entrySet()) { - if (rule.getKey().getState() == ComponentLifecycleState.ACTIVE) { - activeRuleSet.add(rule.getValue()); - } - } - ruleChain = new SimpleRuleActorChain(activeRuleSet); - } -} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java deleted file mode 100644 index e4d023ca90..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/TenantRuleManager.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.shared.rule; - -import akka.actor.ActorContext; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.rule.RuleMetaData; - -public class TenantRuleManager extends RuleManager { - - public TenantRuleManager(ActorSystemContext systemContext, TenantId tenantId) { - super(systemContext, tenantId); - } - - @Override - public void init(ActorContext context) { - if (systemContext.isTenantComponentsInitEnabled()) { - super.init(context); - } - } - - @Override - FetchFunction getFetchRulesFunction() { - return link -> ruleService.findTenantRules(tenantId, link); - } - - @Override - String getDispatcherName() { - return DefaultActorService.TENANT_RULE_DISPATCHER_NAME; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java new file mode 100644 index 0000000000..11ed5a3363 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2018 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.actors.shared.rulechain; + +import akka.actor.ActorRef; +import akka.japi.Creator; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.ruleChain.RuleChainActor; +import org.thingsboard.server.actors.shared.EntityActorsManager; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.rule.RuleChainService; + +/** + * Created by ashvayka on 15.03.18. + */ +@Slf4j +public abstract class RuleChainManager extends EntityActorsManager { + + protected final RuleChainService service; + @Getter + protected RuleChain rootChain; + @Getter + protected ActorRef rootChainActor; + + public RuleChainManager(ActorSystemContext systemContext) { + super(systemContext); + this.service = systemContext.getRuleChainService(); + } + + @Override + public Creator creator(RuleChainId entityId) { + return new RuleChainActor.ActorCreator(systemContext, getTenantId(), entityId); + } + + @Override + public void visit(RuleChain entity, ActorRef actorRef) { + if (entity.isRoot()) { + rootChain = entity; + rootChainActor = actorRef; + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java similarity index 61% rename from application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java index d10731caed..ce1747fc6c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/rule/SystemRuleManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java @@ -13,28 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.shared.rule; +package org.thingsboard.server.actors.shared.rulechain; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.rule.RuleMetaData; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.model.ModelConstants; -public class SystemRuleManager extends RuleManager { +import java.util.Collections; - public SystemRuleManager(ActorSystemContext systemContext) { - super(systemContext, new TenantId(ModelConstants.NULL_UUID)); +public class SystemRuleChainManager extends RuleChainManager { + + public SystemRuleChainManager(ActorSystemContext systemContext) { + super(systemContext); + } + + @Override + protected FetchFunction getFetchEntitiesFunction() { + return link -> new TextPageData<>(Collections.emptyList(), link); } @Override - FetchFunction getFetchRulesFunction() { - return ruleService::findSystemRules; + protected TenantId getTenantId() { + return ModelConstants.SYSTEM_TENANT; } @Override - String getDispatcherName() { + protected String getDispatcherName() { return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME; } } diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java similarity index 72% rename from application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java rename to application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java index 14ea2aa7f8..731d8d8e6c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java +++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.shared.plugin; +package org.thingsboard.server.actors.shared.rulechain; import akka.actor.ActorContext; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction; -import org.thingsboard.server.common.data.plugin.PluginMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; -public class TenantPluginManager extends PluginManager { +public class TenantRuleChainManager extends RuleChainManager { private final TenantId tenantId; - public TenantPluginManager(ActorSystemContext systemContext, TenantId tenantId) { + public TenantRuleChainManager(ActorSystemContext systemContext, TenantId tenantId) { super(systemContext); this.tenantId = tenantId; } @@ -39,18 +39,17 @@ public class TenantPluginManager extends PluginManager { } @Override - FetchFunction getFetchPluginsFunction() { - return link -> pluginService.findTenantPlugins(tenantId, link); - } - - @Override - TenantId getTenantId() { + protected TenantId getTenantId() { return tenantId; } @Override protected String getDispatcherName() { - return DefaultActorService.TENANT_PLUGIN_DISPATCHER_NAME; + return DefaultActorService.TENANT_RULE_DISPATCHER_NAME; } + @Override + protected FetchFunction getFetchEntitiesFunction() { + return link -> service.findTenantRuleChains(tenantId, link); + } } diff --git a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java index ccc31cca29..8623370896 100644 --- a/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/stats/StatsActor.java @@ -24,6 +24,7 @@ import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; public class StatsActor extends ContextAwareActor { @@ -35,6 +36,12 @@ public class StatsActor extends ContextAwareActor { super(context); } + @Override + protected boolean process(TbActorMsg msg) { + //TODO Move everything here, to work with TbActorMsg\ + return false; + } + @Override public void onReceive(Object msg) throws Exception { logger.debug("Received message: {}", msg); diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java b/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java deleted file mode 100644 index a84e0b5fd4..0000000000 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/RuleChainDeviceMsg.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors.tenant; - -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; - -public class RuleChainDeviceMsg { - - private final ToDeviceActorMsg toDeviceActorMsg; - private final RuleActorChain ruleChain; - - public RuleChainDeviceMsg(ToDeviceActorMsg toDeviceActorMsg, RuleActorChain ruleChain) { - super(); - this.toDeviceActorMsg = toDeviceActorMsg; - this.ruleChain = ruleChain; - } - - public ToDeviceActorMsg getToDeviceActorMsg() { - return toDeviceActorMsg; - } - - public RuleActorChain getRuleChain() { - return ruleChain; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index b923fe15a3..7a3127dd36 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -15,61 +15,55 @@ */ package org.thingsboard.server.actors.tenant; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - +import akka.actor.ActorRef; +import akka.actor.OneForOneStrategy; +import akka.actor.Props; +import akka.actor.SupervisorStrategy; +import akka.japi.Function; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.device.DeviceActor; -import org.thingsboard.server.actors.plugin.PluginTerminationMsg; -import org.thingsboard.server.actors.rule.ComplexRuleActorChain; -import org.thingsboard.server.actors.rule.RuleActorChain; -import org.thingsboard.server.actors.service.ContextAwareActor; +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; +import org.thingsboard.server.actors.ruleChain.RuleChainToRuleChainMsg; import org.thingsboard.server.actors.service.ContextBasedCreator; import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.actors.shared.plugin.PluginManager; -import org.thingsboard.server.actors.shared.plugin.TenantPluginManager; -import org.thingsboard.server.actors.shared.rule.RuleManager; -import org.thingsboard.server.actors.shared.rule.TenantRuleManager; +import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; - -import akka.actor.ActorRef; -import akka.actor.Props; -import akka.event.Logging; -import akka.event.LoggingAdapter; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import scala.concurrent.duration.Duration; -public class TenantActor extends ContextAwareActor { +import java.util.HashMap; +import java.util.Map; - private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); +public class TenantActor extends RuleChainManagerActor { private final TenantId tenantId; - private final RuleManager ruleManager; - private final PluginManager pluginManager; private final Map deviceActors; private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { - super(systemContext); + super(systemContext, new TenantRuleChainManager(systemContext, tenantId)); this.tenantId = tenantId; - this.ruleManager = new TenantRuleManager(systemContext, tenantId); - this.pluginManager = new TenantPluginManager(systemContext, tenantId); this.deviceActors = new HashMap<>(); } + + @Override + public SupervisorStrategy supervisorStrategy() { + return strategy; + } + @Override public void preStart() { logger.info("[{}] Starting tenant actor.", tenantId); try { - ruleManager.init(this.context()); - pluginManager.init(this.context()); + initRuleChains(); logger.info("[{}] Tenant actor started.", tenantId); } catch (Exception e) { logger.error(e, "[{}] Unknown failure", tenantId); @@ -77,89 +71,74 @@ public class TenantActor extends ContextAwareActor { } @Override - public void onReceive(Object msg) throws Exception { - logger.debug("[{}] Received message: {}", tenantId, msg); - if (msg instanceof RuleChainDeviceMsg) { - process((RuleChainDeviceMsg) msg); - } else if (msg instanceof ToDeviceActorMsg) { - onToDeviceActorMsg((ToDeviceActorMsg) msg); - } else if (msg instanceof ToPluginActorMsg) { - onToPluginMsg((ToPluginActorMsg) msg); - } else if (msg instanceof ToRuleActorMsg) { - onToRuleMsg((ToRuleActorMsg) msg); - } else if (msg instanceof ToDeviceActorNotificationMsg) { - onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg); - } else if (msg instanceof ClusterEventMsg) { - broadcast(msg); - } else if (msg instanceof ComponentLifecycleMsg) { - onComponentLifecycleMsg((ComponentLifecycleMsg) msg); - } else if (msg instanceof PluginTerminationMsg) { - onPluginTerminated((PluginTerminationMsg) msg); - } else { - logger.warning("[{}] Unknown message: {}!", tenantId, msg); + protected boolean process(TbActorMsg msg) { + switch (msg.getMsgType()) { + case CLUSTER_EVENT_MSG: + broadcast(msg); + break; + case COMPONENT_LIFE_CYCLE_MSG: + onComponentLifecycleMsg((ComponentLifecycleMsg) msg); + break; + case SERVICE_TO_RULE_ENGINE_MSG: + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); + break; + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG: + onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); + break; + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG: + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: + onToDeviceActorMsg((DeviceAwareMsg) msg); + break; + case RULE_CHAIN_TO_RULE_CHAIN_MSG: + onRuleChainMsg((RuleChainToRuleChainMsg) msg); + break; + default: + return false; } + return true; } - private void broadcast(Object msg) { - pluginManager.broadcast(msg); + @Override + protected void broadcast(Object msg) { + super.broadcast(msg); deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); } - private void onToDeviceActorMsg(ToDeviceActorMsg msg) { - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { + ruleChainManager.getRootChainActor().tell(msg, self()); } - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); + private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) { + ruleChainManager.getRootChainActor().tell(msg, self()); } - private void onToRuleMsg(ToRuleActorMsg msg) { - ActorRef target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId()); - target.tell(msg, ActorRef.noSender()); + private void onRuleChainMsg(RuleChainToRuleChainMsg msg) { + ruleChainManager.getOrCreateActor(context(), msg.getTarget()).tell(msg, self()); } - private void onToPluginMsg(ToPluginActorMsg msg) { - if (msg.getPluginTenantId().equals(tenantId)) { - ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId()); - pluginActor.tell(msg, ActorRef.noSender()); - } else { - context().parent().tell(msg, ActorRef.noSender()); - } + + private void onToDeviceActorMsg(DeviceAwareMsg msg) { + getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); } private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { - Optional pluginId = msg.getPluginId(); - Optional ruleId = msg.getRuleId(); - if (pluginId.isPresent()) { - ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get()); - pluginActor.tell(msg, ActorRef.noSender()); - } else if (ruleId.isPresent()) { - ActorRef target; - Optional ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent()); - if (ref.isPresent()) { - target = ref.get(); - } else { - logger.debug("Failed to find actor for rule: [{}]", ruleId); - return; + ActorRef target = getEntityActorRef(msg.getEntityId()); + if (target != null) { + if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { + RuleChain ruleChain = systemContext.getRuleChainService(). + findRuleChainById(new RuleChainId(msg.getEntityId().getId())); + ruleChainManager.visit(ruleChain, target); } target.tell(msg, ActorRef.noSender()); } else { - logger.debug("[{}] Invalid component lifecycle msg.", tenantId); + logger.debug("Invalid component lifecycle msg: {}", msg); } } - private void onPluginTerminated(PluginTerminationMsg msg) { - pluginManager.remove(msg.getId()); - } - - private void process(RuleChainDeviceMsg msg) { - ToDeviceActorMsg toDeviceActorMsg = msg.getToDeviceActorMsg(); - ActorRef deviceActor = getOrCreateDeviceActor(toDeviceActorMsg.getDeviceId()); - RuleActorChain tenantChain = ruleManager.getRuleChain(this.context()); - RuleActorChain chain = new ComplexRuleActorChain(msg.getRuleChain(), tenantChain); - deviceActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, chain), context().self()); - } - private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId)) .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString())); @@ -181,4 +160,12 @@ public class TenantActor extends ContextAwareActor { } } + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function() { + @Override + public SupervisorStrategy.Directive apply(Throwable t) { + logger.error(t, "Unknown failure"); + return SupervisorStrategy.resume(); + } + }); + } diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java index 34afd35ba2..4c36a15758 100644 --- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java +++ b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java @@ -17,8 +17,6 @@ package org.thingsboard.server.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.audit.ActionType; import java.util.HashMap; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 87b187e98c..2254cf3da0 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -24,7 +24,11 @@ import org.springframework.context.annotation.Configuration; import org.thingsboard.server.common.data.security.Authority; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.schema.AlternateTypeRule; -import springfox.documentation.service.*; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.Docket; diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 2952529812..6afa6b2460 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -37,15 +36,18 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.web.cors.CorsUtils; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.thingsboard.server.dao.audit.AuditLogLevelFilter; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; +import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; +import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter; +import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider; +import org.thingsboard.server.service.security.auth.jwt.RefreshTokenProcessingFilter; +import org.thingsboard.server.service.security.auth.jwt.SkipPathRequestMatcher; +import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationProvider; import org.thingsboard.server.service.security.auth.rest.RestLoginProcessingFilter; -import org.thingsboard.server.service.security.auth.jwt.*; -import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; import org.thingsboard.server.service.security.auth.rest.RestPublicLoginProcessingFilter; import java.util.ArrayList; diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index a75ecb1353..59b7da2e76 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -15,12 +15,6 @@ */ package org.thingsboard.server.config; -import java.util.Map; - -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; -import org.thingsboard.server.controller.plugin.PluginWebSocketHandler; -import org.thingsboard.server.service.security.model.SecurityUser; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -35,6 +29,12 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry import org.springframework.web.socket.server.HandshakeInterceptor; import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.controller.plugin.TbWebSocketHandler; +import org.thingsboard.server.service.security.model.SecurityUser; + +import java.util.Map; @Configuration @EnableWebSocket @@ -54,7 +54,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(pluginWsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") + registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { @Override @@ -82,8 +82,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer { } @Bean - public WebSocketHandler pluginWsHandler() { - return new PluginWebSocketHandler(); + public WebSocketHandler wsHandler() { + return new TbWebSocketHandler(); } protected SecurityUser getCurrentUser() throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index e9a6ba3816..0cf149114a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -17,11 +17,16 @@ package org.thingsboard.server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.dao.settings.AdminSettingsService; -import org.thingsboard.server.exception.ThingsboardException; -import org.thingsboard.server.service.mail.MailService; import org.thingsboard.server.service.update.UpdateService; import org.thingsboard.server.service.update.model.UpdateMessage; diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 1959f4e528..28cc2fdf09 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -18,13 +18,27 @@ package org.thingsboard.server.controller; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.thingsboard.server.common.data.alarm.*; -import org.thingsboard.server.common.data.id.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmId; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmQuery; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; @RestController @RequestMapping("/api") @@ -93,7 +107,7 @@ public class AlarmController extends BaseController { try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); checkAlarmId(alarmId); - alarmService.clearAlarm(alarmId, System.currentTimeMillis()).get(); + alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get(); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 9b439133d3..35865ab2df 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -18,23 +18,30 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.asset.AssetSearchQuery; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.ArrayList; diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index 75bcf2ab15..34bcf8456f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -16,15 +16,20 @@ package org.thingsboard.server.controller; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +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.thingsboard.server.common.data.audit.AuditLog; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.exception.ThingsboardException; import java.util.UUID; diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index ef38d802ec..af24c9c24b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -25,12 +25,18 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; -import org.thingsboard.server.service.mail.MailService; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; 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 3264af40b7..f04422898c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -15,12 +15,13 @@ */ package org.thingsboard.server.controller; -import com.fasterxml.jackson.databind.JsonNode; +import com.datastax.driver.core.utils.UUIDs; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -30,18 +31,24 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmId; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.page.TextPageLink; 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.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.audit.AuditLogService; @@ -52,23 +59,23 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.plugin.PluginService; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; -import org.thingsboard.server.exception.ThingsboardErrorCode; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; -import org.thingsboard.server.exception.ThingsboardException; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.state.DeviceStateService; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -79,9 +86,14 @@ public abstract class BaseController { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION = "You don't have permission to perform this operation!"; + private static final ObjectMapper json = new ObjectMapper(); + @Autowired private ThingsboardErrorResponseHandler errorResponseHandler; + @Autowired + protected TenantService tenantService; + @Autowired protected CustomerService customerService; @@ -113,10 +125,7 @@ public abstract class BaseController { protected ComponentDiscoveryService componentDescriptorService; @Autowired - protected RuleService ruleService; - - @Autowired - protected PluginService pluginService; + protected RuleChainService ruleChainService; @Autowired protected ActorService actorService; @@ -127,6 +136,9 @@ public abstract class BaseController { @Autowired protected AuditLogService auditLogService; + @Autowired + protected DeviceStateService deviceStateService; + @ExceptionHandler(ThingsboardException.class) public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { errorResponseHandler.handle(ex, response); @@ -289,11 +301,8 @@ public abstract class BaseController { case TENANT: checkTenantId(new TenantId(entityId.getId())); return; - case PLUGIN: - checkPlugin(new PluginId(entityId.getId())); - return; - case RULE: - checkRule(new RuleId(entityId.getId())); + case RULE_CHAIN: + checkRuleChain(new RuleChainId(entityId.getId())); return; case ASSET: checkAsset(assetService.findAssetById(new AssetId(entityId.getId()))); @@ -472,60 +481,34 @@ public abstract class BaseController { } } - List checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException { + List checkComponentDescriptorsByTypes(Set types) throws ThingsboardException { try { - checkComponentDescriptorByClazz(pluginClazz); - log.debug("[{}] Lookup plugin actions", pluginClazz); - return componentDescriptorService.getPluginActions(pluginClazz); + log.debug("[{}] Lookup component descriptors", types); + return componentDescriptorService.getComponents(types); } catch (Exception e) { throw handleException(e, false); } } - protected PluginMetaData checkPlugin(PluginMetaData plugin) throws ThingsboardException { - checkNotNull(plugin); - SecurityUser authUser = getCurrentUser(); - TenantId tenantId = plugin.getTenantId(); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - if (authUser.getAuthority() != Authority.SYS_ADMIN) { - if (authUser.getTenantId() == null || - !tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) { - throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, - ThingsboardErrorCode.PERMISSION_DENIED); - - } else if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { - plugin.setConfiguration(null); - } - } - return plugin; - } - - protected PluginMetaData checkPlugin(PluginId pluginId) throws ThingsboardException { - checkNotNull(pluginId); - return checkPlugin(pluginService.findPluginById(pluginId)); - } - - protected RuleMetaData checkRule(RuleId ruleId) throws ThingsboardException { - checkNotNull(ruleId); - return checkRule(ruleService.findRuleById(ruleId)); + protected RuleChain checkRuleChain(RuleChainId ruleChainId) throws ThingsboardException { + checkNotNull(ruleChainId); + return checkRuleChain(ruleChainService.findRuleChainById(ruleChainId)); } - protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException { - checkNotNull(rule); + protected RuleChain checkRuleChain(RuleChain ruleChain) throws ThingsboardException { + checkNotNull(ruleChain); SecurityUser authUser = getCurrentUser(); - TenantId tenantId = rule.getTenantId(); + TenantId tenantId = ruleChain.getTenantId(); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - if (authUser.getAuthority() != Authority.SYS_ADMIN) { - if (authUser.getTenantId() == null || - !tenantId.getId().equals(ModelConstants.NULL_UUID) && !authUser.getTenantId().equals(tenantId)) { - throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, - ThingsboardErrorCode.PERMISSION_DENIED); - - } + if (authUser.getAuthority() != Authority.TENANT_ADMIN || + !authUser.getTenantId().equals(tenantId)) { + throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, + ThingsboardErrorCode.PERMISSION_DENIED); } - return rule; + return ruleChain; } + protected String constructBaseUrl(HttpServletRequest request) { String scheme = request.getScheme(); if (request.getHeader("x-forwarded-proto") != null) { @@ -553,12 +536,127 @@ public abstract class BaseController { protected & HasName, I extends UUIDBased & EntityId> void logEntityAction(I entityId, E entity, CustomerId customerId, ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { - User user = getCurrentUser(); + logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo); + } + + protected & HasName, + I extends UUIDBased & EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId, + ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { if (customerId == null || customerId.isNullUid()) { customerId = user.getCustomerId(); } + if (e == null) { + pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); + } auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } + public static Exception toException(Throwable error) { + return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; + } + + private & HasName, + I extends UUIDBased & EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, + ActionType actionType, Object... additionalInfo) { + String msgType = null; + switch (actionType) { + case ADDED: + msgType = DataConstants.ENTITY_CREATED; + break; + case DELETED: + msgType = DataConstants.ENTITY_DELETED; + break; + case UPDATED: + msgType = DataConstants.ENTITY_UPDATED; + break; + case ASSIGNED_TO_CUSTOMER: + msgType = DataConstants.ENTITY_ASSIGNED; + break; + case UNASSIGNED_FROM_CUSTOMER: + msgType = DataConstants.ENTITY_UNASSIGNED; + break; + case ATTRIBUTES_UPDATED: + msgType = DataConstants.ATTRIBUTES_UPDATED; + break; + case ATTRIBUTES_DELETED: + msgType = DataConstants.ATTRIBUTES_DELETED; + break; + } + if (!StringUtils.isEmpty(msgType)) { + try { + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("userId", user.getId().toString()); + metaData.putValue("userName", user.getName()); + if (customerId != null && !customerId.isNullUid()) { + metaData.putValue("customerId", customerId.toString()); + } + if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { + String strCustomerId = extractParameter(String.class, 1, additionalInfo); + String strCustomerName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("assignedCustomerId", strCustomerId); + metaData.putValue("assignedCustomerName", strCustomerName); + } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { + String strCustomerId = extractParameter(String.class, 1, additionalInfo); + String strCustomerName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("unassignedCustomerId", strCustomerId); + metaData.putValue("unassignedCustomerName", strCustomerName); + } + ObjectNode entityNode; + if (entity != null) { + entityNode = json.valueToTree(entity); + if (entityId.getEntityType() == EntityType.DASHBOARD) { + entityNode.put("configuration", ""); + } + } else { + entityNode = json.createObjectNode(); + if (actionType == ActionType.ATTRIBUTES_UPDATED) { + String scope = extractParameter(String.class, 0, additionalInfo); + List attributes = extractParameter(List.class, 1, additionalInfo); + metaData.putValue("scope", scope); + if (attributes != null) { + for (AttributeKvEntry attr : attributes) { + if (attr.getDataType() == DataType.BOOLEAN) { + entityNode.put(attr.getKey(), attr.getBooleanValue().get()); + } else if (attr.getDataType() == DataType.DOUBLE) { + entityNode.put(attr.getKey(), attr.getDoubleValue().get()); + } else if (attr.getDataType() == DataType.LONG) { + entityNode.put(attr.getKey(), attr.getLongValue().get()); + } else { + entityNode.put(attr.getKey(), attr.getValueAsString()); + } + } + } + } else if (actionType == ActionType.ATTRIBUTES_DELETED) { + String scope = extractParameter(String.class, 0, additionalInfo); + List keys = extractParameter(List.class, 1, additionalInfo); + metaData.putValue("scope", scope); + ArrayNode attrsArrayNode = entityNode.putArray("attributes"); + if (keys != null) { + keys.forEach(attrsArrayNode::add); + } + } + } + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, entityId, metaData, TbMsgDataType.JSON + , json.writeValueAsString(entityNode) + , null, null, 0L); + actorService.onMsg(new ServiceToRuleEngineMsg(user.getTenantId(), tbMsg)); + } catch (Exception e) { + log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); + } + } + } + + private T extractParameter(Class clazz, int index, Object... additionalInfo) { + T result = null; + if (additionalInfo != null && additionalInfo.length > index) { + Object paramObject = additionalInfo[index]; + if (clazz.isInstance(paramObject)) { + result = clazz.cast(paramObject); + } + } + return result; + } + + } diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java index e63a443769..fe4829f789 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java @@ -16,12 +16,19 @@ package org.thingsboard.server.controller; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +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.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.exception.ThingsboardException; +import java.util.HashSet; import java.util.List; +import java.util.Set; @RestController @RequestMapping("/api") @@ -52,12 +59,16 @@ public class ComponentDescriptorController extends BaseController { } @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") - @RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET) + @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET) @ResponseBody - public List getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException { - checkParameter("pluginClazz", pluginClazz); + public List getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException { + checkArrayParameter("componentTypes", strComponentTypes); try { - return checkPluginActionsByPluginClazz(pluginClazz); + Set componentTypes = new HashSet<>(); + for (String strComponentType : strComponentTypes) { + componentTypes.add(ComponentType.valueOf(strComponentType)); + } + return checkComponentDescriptorsByTypes(componentTypes); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index b16470216a..34843ceb01 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -20,15 +20,22 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.exception.ThingsboardException; @RestController @RequestMapping("/api") diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index d2952a16cb..85227e76a9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -17,9 +17,21 @@ package org.thingsboard.server.controller; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.thingsboard.server.common.data.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; @@ -27,9 +39,6 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; -import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardException; import java.util.HashSet; import java.util.Set; 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 bceea54135..a6daeecf4a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -18,14 +18,22 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.device.DeviceSearchQuery; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -35,8 +43,6 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.ArrayList; @@ -90,6 +96,11 @@ public class DeviceController extends BaseController { savedDevice.getCustomerId(), device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + if (device.getId() == null) { + deviceStateService.onDeviceAdded(savedDevice); + } else { + deviceStateService.onDeviceUpdated(savedDevice); + } return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), device, @@ -112,6 +123,7 @@ public class DeviceController extends BaseController { device.getCustomerId(), ActionType.DELETED, null, strDeviceId); + deviceStateService.onDeviceDeleted(device); } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), null, diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 03054df985..844dbd35a9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -17,15 +17,21 @@ package org.thingsboard.server.controller; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationInfo; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index 331b15eb09..7c8b2ae45e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -17,15 +17,21 @@ package org.thingsboard.server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PathVariable; +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.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; @RestController @RequestMapping("/api") diff --git a/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java b/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java new file mode 100644 index 0000000000..fb1f3e7bee --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/HttpValidationCallback.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.google.common.util.concurrent.FutureCallback; +import org.springframework.http.ResponseEntity; +import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.server.service.security.ValidationCallback; + +/** + * Created by ashvayka on 21.02.17. + */ +public class HttpValidationCallback extends ValidationCallback> { + + public HttpValidationCallback(DeferredResult response, FutureCallback> action) { + super(response, action); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/controller/PluginController.java b/application/src/main/java/org/thingsboard/server/controller/PluginController.java deleted file mode 100644 index 2c69248faa..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/PluginController.java +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller; - -import org.springframework.http.HttpStatus; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardException; - -import java.util.List; - -@RestController -@RequestMapping("/api") -public class PluginController extends BaseController { - - public static final String PLUGIN_ID = "pluginId"; - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.GET) - @ResponseBody - public PluginMetaData getPluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException { - checkParameter(PLUGIN_ID, strPluginId); - try { - PluginId pluginId = new PluginId(toUUID(strPluginId)); - return checkPlugin(pluginService.findPluginById(pluginId)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin/token/{pluginToken}", method = RequestMethod.GET) - @ResponseBody - public PluginMetaData getPluginByToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException { - checkParameter("pluginToken", pluginToken); - try { - return checkPlugin(pluginService.findPluginByApiToken(pluginToken)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin", method = RequestMethod.POST) - @ResponseBody - public PluginMetaData savePlugin(@RequestBody PluginMetaData source) throws ThingsboardException { - try { - boolean created = source.getId() == null; - source.setTenantId(getCurrentUser().getTenantId()); - PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source)); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - - logEntityAction(plugin.getId(), plugin, - null, - created ? ActionType.ADDED : ActionType.UPDATED, null); - - return plugin; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.PLUGIN), source, - null, source.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin/{pluginId}/activate", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void activatePluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException { - checkParameter(PLUGIN_ID, strPluginId); - try { - PluginId pluginId = new PluginId(toUUID(strPluginId)); - PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); - pluginService.activatePluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED); - - logEntityAction(plugin.getId(), plugin, - null, - ActionType.ACTIVATED, null, strPluginId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.PLUGIN), - null, - null, - ActionType.ACTIVATED, e, strPluginId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin/{pluginId}/suspend", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void suspendPluginById(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException { - checkParameter(PLUGIN_ID, strPluginId); - try { - PluginId pluginId = new PluginId(toUUID(strPluginId)); - PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); - pluginService.suspendPluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED); - - logEntityAction(plugin.getId(), plugin, - null, - ActionType.SUSPENDED, null, strPluginId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.PLUGIN), - null, - null, - ActionType.SUSPENDED, e, strPluginId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/plugin/system", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getSystemPlugins( - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - try { - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(pluginService.findSystemPlugins(pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/plugin/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getTenantPlugins( - @PathVariable("tenantId") String strTenantId, - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - checkParameter("tenantId", strTenantId); - try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugins", method = RequestMethod.GET) - @ResponseBody - public List getPlugins() throws ThingsboardException { - try { - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { - return checkNotNull(pluginService.findSystemPlugins()); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - List plugins = checkNotNull(pluginService.findAllTenantPluginsByTenantId(tenantId)); - plugins.stream() - .filter(plugin -> plugin.getTenantId().getId().equals(ModelConstants.NULL_UUID)) - .forEach(plugin -> plugin.setConfiguration(null)); - return plugins; - } - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/plugin", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getTenantPlugins( - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(pluginService.findTenantPlugins(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/plugin/{pluginId}", method = RequestMethod.DELETE) - @ResponseStatus(value = HttpStatus.OK) - public void deletePlugin(@PathVariable(PLUGIN_ID) String strPluginId) throws ThingsboardException { - checkParameter(PLUGIN_ID, strPluginId); - try { - PluginId pluginId = new PluginId(toUUID(strPluginId)); - PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId)); - pluginService.deletePluginById(pluginId); - actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED); - - logEntityAction(pluginId, plugin, - null, - ActionType.DELETED, null, strPluginId); - - } catch (Exception e) { - logEntityAction(emptyId(EntityType.PLUGIN), - null, - null, - ActionType.DELETED, e, strPluginId); - throw handleException(e); - } - } - - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcController.java b/application/src/main/java/org/thingsboard/server/controller/RpcController.java new file mode 100644 index 0000000000..e39a3f6c34 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/RpcController.java @@ -0,0 +1,217 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.util.concurrent.FutureCallback; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +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.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.rule.engine.api.RpcError; +import org.thingsboard.server.common.data.audit.ActionType; +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.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.rpc.RpcRequest; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.service.rpc.DeviceRpcService; +import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.service.rpc.LocalRequestMetaData; +import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by ashvayka on 22.03.18. + */ +@RestController +@RequestMapping(TbUrlConstants.RPC_URL_PREFIX) +@Slf4j +public class RpcController extends BaseController { + + public static final int DEFAULT_TIMEOUT = 10000; + protected final ObjectMapper jsonMapper = new ObjectMapper(); + + @Autowired + private DeviceRpcService deviceRpcService; + + @Autowired + private AccessValidator accessValidator; + + private ExecutorService executor; + + @PostConstruct + public void initExecutor() { + executor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException { + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); + } + + + private DeferredResult handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { + try { + JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); + RpcRequest cmd = new RpcRequest(rpcRequestBody.get("method").asText(), + jsonMapper.writeValueAsString(rpcRequestBody.get("params"))); + + if (rpcRequestBody.has("timeout")) { + cmd.setTimeout(rpcRequestBody.get("timeout").asLong()); + } + SecurityUser currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + final DeferredResult response = new DeferredResult<>(); + long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT); + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData()); + accessValidator.validate(currentUser, deviceId, new HttpValidationCallback(response, new FutureCallback>() { + @Override + public void onSuccess(@Nullable DeferredResult result) { + ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(), + tenantId, + deviceId, + oneWay, + timeout, + body + ); + deviceRpcService.processRpcRequestToDevice(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse)); + } + + @Override + public void onFailure(Throwable e) { + ResponseEntity entity; + if (e instanceof ToErrorResponseEntity) { + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); + } else { + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); + } + logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e); + response.setResult(entity); + } + })); + return response; + } catch (IOException ioe) { + throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + } + + public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) { + Optional rpcError = response.getError(); + DeferredResult responseWriter = rpcRequest.getResponseWriter(); + if (rpcError.isPresent()) { + logRpcCall(rpcRequest, rpcError, null); + RpcError error = rpcError.get(); + switch (error) { + case TIMEOUT: + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); + break; + case NO_ACTIVE_CONNECTION: + responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT)); + break; + default: + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); + break; + } + } else { + Optional responseData = response.getResponse(); + if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) { + String data = responseData.get(); + try { + logRpcCall(rpcRequest, rpcError, null); + responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK)); + } catch (IOException e) { + log.debug("Failed to decode device response: {}", data, e); + logRpcCall(rpcRequest, rpcError, e); + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE)); + } + } else { + logRpcCall(rpcRequest, rpcError, null); + responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); + } + } + } + + private void logRpcCall(LocalRequestMetaData rpcRequest, Optional rpcError, Throwable e) { + logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null); + } + + + private void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional rpcError, Throwable e) { + String rpcErrorStr = ""; + if (rpcError.isPresent()) { + rpcErrorStr = "RPC Error: " + rpcError.get().name(); + } + String method = body.getMethod(); + String params = body.getParams(); + + auditLogService.logEntityAction( + user.getTenantId(), + user.getCustomerId(), + user.getId(), + user.getName(), + (UUIDBased & EntityId) entityId, + null, + ActionType.RPC_CALL, + BaseController.toException(e), + rpcErrorStr, + oneWay, + method, + params); + } + + +} diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java new file mode 100644 index 0000000000..86b8fdacd7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -0,0 +1,334 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.service.script.JsSandboxService; +import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +@RestController +@RequestMapping("/api") +public class RuleChainController extends BaseController { + + public static final String RULE_CHAIN_ID = "ruleChainId"; + public static final String RULE_NODE_ID = "ruleNodeId"; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Autowired + private EventService eventService; + + @Autowired + private JsSandboxService jsSandboxService; + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET) + @ResponseBody + public RuleChain getRuleChainById(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { + checkParameter(RULE_CHAIN_ID, strRuleChainId); + try { + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + return checkRuleChain(ruleChainId); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET) + @ResponseBody + public RuleChainMetaData getRuleChainMetaData(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { + checkParameter(RULE_CHAIN_ID, strRuleChainId); + try { + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + checkRuleChain(ruleChainId); + return ruleChainService.loadRuleChainMetaData(ruleChainId); + } catch (Exception e) { + throw handleException(e); + } + } + + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain", method = RequestMethod.POST) + @ResponseBody + public RuleChain saveRuleChain(@RequestBody RuleChain ruleChain) throws ThingsboardException { + try { + boolean created = ruleChain.getId() == null; + ruleChain.setTenantId(getCurrentUser().getTenantId()); + RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); + + actorService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(), + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + + logEntityAction(savedRuleChain.getId(), savedRuleChain, + null, + created ? ActionType.ADDED : ActionType.UPDATED, null); + + return savedRuleChain; + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, + null, ruleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); + + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST) + @ResponseBody + public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { + checkParameter(RULE_CHAIN_ID, strRuleChainId); + try { + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId); + TenantId tenantId = getCurrentUser().getTenantId(); + RuleChain previousRootRuleChain = ruleChainService.getRootTenantRuleChain(tenantId); + if (ruleChainService.setRootRuleChain(ruleChainId)) { + + previousRootRuleChain = ruleChainService.findRuleChainById(previousRootRuleChain.getId()); + + actorService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(), + ComponentLifecycleEvent.UPDATED); + + logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain, + null, ActionType.UPDATED, null); + + ruleChain = ruleChainService.findRuleChainById(ruleChainId); + + actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), + ComponentLifecycleEvent.UPDATED); + + logEntityAction(ruleChain.getId(), ruleChain, + null, ActionType.UPDATED, null); + + } + return ruleChain; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.RULE_CHAIN), + null, + null, + ActionType.UPDATED, e, strRuleChainId); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST) + @ResponseBody + public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { + try { + RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId()); + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(ruleChainMetaData)); + + actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); + + logEntityAction(ruleChain.getId(), ruleChain, + null, + ActionType.UPDATED, null, ruleChainMetaData); + + return savedRuleChainMetaData; + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, + null, ActionType.UPDATED, e, ruleChainMetaData); + + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChains", params = {"limit"}, method = RequestMethod.GET) + @ResponseBody + public TextPageData getRuleChains( + @RequestParam int limit, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String idOffset, + @RequestParam(required = false) String textOffset) throws ThingsboardException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); + return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) + @ResponseStatus(value = HttpStatus.OK) + public void deleteRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { + checkParameter(RULE_CHAIN_ID, strRuleChainId); + try { + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); + RuleChain ruleChain = checkRuleChain(ruleChainId); + + ruleChainService.deleteRuleChainById(ruleChainId); + + actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); + + logEntityAction(ruleChainId, ruleChain, + null, + ActionType.DELETED, null, strRuleChainId); + + } catch (Exception e) { + logEntityAction(emptyId(EntityType.RULE_CHAIN), + null, + null, + ActionType.DELETED, e, strRuleChainId); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET) + @ResponseBody + public JsonNode getLatestRuleNodeDebugInput(@PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException { + checkParameter(RULE_NODE_ID, strRuleNodeId); + try { + RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId)); + TenantId tenantId = getCurrentUser().getTenantId(); + List events = eventService.findLatestEvents(tenantId, ruleNodeId, DataConstants.DEBUG_RULE_NODE, 2); + JsonNode result = null; + if (events != null) { + for (Event event : events) { + JsonNode body = event.getBody(); + if (body.has("type") && body.get("type").asText().equals("IN")) { + result = body; + break; + } + } + } + return result; + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/ruleChain/testScript", method = RequestMethod.POST) + @ResponseBody + public JsonNode testScript(@RequestBody JsonNode inputParams) throws ThingsboardException { + try { + String script = inputParams.get("script").asText(); + String scriptType = inputParams.get("scriptType").asText(); + JsonNode argNamesJson = inputParams.get("argNames"); + String[] argNames = objectMapper.treeToValue(argNamesJson, String[].class); + + String data = inputParams.get("msg").asText(); + JsonNode metadataJson = inputParams.get("metadata"); + Map metadata = objectMapper.convertValue(metadataJson, new TypeReference>() {}); + String msgType = inputParams.get("msgType").asText(); + String output = ""; + String errorText = ""; + ScriptEngine engine = null; + try { + engine = new RuleNodeJsScriptEngine(jsSandboxService, script, argNames); + TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L); + switch (scriptType) { + case "update": + output = msgToOutput(engine.executeUpdate(inMsg)); + break; + case "generate": + output = msgToOutput(engine.executeGenerate(inMsg)); + break; + case "filter": + boolean result = engine.executeFilter(inMsg); + output = Boolean.toString(result); + break; + case "switch": + Set states = engine.executeSwitch(inMsg); + output = objectMapper.writeValueAsString(states); + break; + case "json": + JsonNode json = engine.executeJson(inMsg); + output = objectMapper.writeValueAsString(json); + break; + case "string": + output = engine.executeToString(inMsg); + break; + default: + throw new IllegalArgumentException("Unsupported script type: " + scriptType); + } + } catch (Exception e) { + log.error("Error evaluating JS function", e); + errorText = e.getMessage(); + } finally { + if (engine != null) { + engine.destroy(); + } + } + ObjectNode result = objectMapper.createObjectNode(); + result.put("output", output); + result.put("error", errorText); + return result; + } catch (Exception e) { + throw handleException(e); + } + } + + private String msgToOutput(TbMsg msg) throws Exception { + ObjectNode msgData = objectMapper.createObjectNode(); + if (!StringUtils.isEmpty(msg.getData())) { + msgData.set("msg", objectMapper.readTree(msg.getData())); + } + Map metadata = msg.getMetaData().getData(); + msgData.set("metadata", objectMapper.valueToTree(metadata)); + msgData.put("msgType", msg.getType()); + return objectMapper.writeValueAsString(msgData); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleController.java b/application/src/main/java/org/thingsboard/server/controller/RuleController.java deleted file mode 100644 index e498c8fffc..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/RuleController.java +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller; - -import org.springframework.http.HttpStatus; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.exception.ThingsboardException; - -import java.util.List; - -@RestController -@RequestMapping("/api") -public class RuleController extends BaseController { - - public static final String RULE_ID = "ruleId"; - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.GET) - @ResponseBody - public RuleMetaData getRuleById(@PathVariable(RULE_ID) String strRuleId) throws ThingsboardException { - checkParameter(RULE_ID, strRuleId); - try { - RuleId ruleId = new RuleId(toUUID(strRuleId)); - return checkRule(ruleService.findRuleById(ruleId)); - } catch (Exception e) { - throw handleException(e); - } - } - - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule/token/{pluginToken}", method = RequestMethod.GET) - @ResponseBody - public List getRulesByPluginToken(@PathVariable("pluginToken") String pluginToken) throws ThingsboardException { - checkParameter("pluginToken", pluginToken); - try { - PluginMetaData plugin = checkPlugin(pluginService.findPluginByApiToken(pluginToken)); - return ruleService.findPluginRules(plugin.getApiToken()); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule", method = RequestMethod.POST) - @ResponseBody - public RuleMetaData saveRule(@RequestBody RuleMetaData source) throws ThingsboardException { - try { - boolean created = source.getId() == null; - source.setTenantId(getCurrentUser().getTenantId()); - RuleMetaData rule = checkNotNull(ruleService.saveRule(source)); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - - logEntityAction(rule.getId(), rule, - null, - created ? ActionType.ADDED : ActionType.UPDATED, null); - - return rule; - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE), source, - null, source.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule/{ruleId}/activate", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void activateRuleById(@PathVariable(RULE_ID) String strRuleId) throws ThingsboardException { - checkParameter(RULE_ID, strRuleId); - try { - RuleId ruleId = new RuleId(toUUID(strRuleId)); - RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); - ruleService.activateRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED); - - logEntityAction(rule.getId(), rule, - null, - ActionType.ACTIVATED, null, strRuleId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE), - null, - null, - ActionType.ACTIVATED, e, strRuleId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule/{ruleId}/suspend", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public void suspendRuleById(@PathVariable(RULE_ID) String strRuleId) throws ThingsboardException { - checkParameter(RULE_ID, strRuleId); - try { - RuleId ruleId = new RuleId(toUUID(strRuleId)); - RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); - ruleService.suspendRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED); - - logEntityAction(rule.getId(), rule, - null, - ActionType.SUSPENDED, null, strRuleId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE), - null, - null, - ActionType.SUSPENDED, e, strRuleId); - - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/rule/system", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getSystemRules( - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - try { - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(ruleService.findSystemRules(pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/rule/tenant/{tenantId}", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getTenantRules( - @PathVariable("tenantId") String strTenantId, - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - checkParameter("tenantId", strTenantId); - try { - TenantId tenantId = new TenantId(toUUID(strTenantId)); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(ruleService.findTenantRules(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rules", method = RequestMethod.GET) - @ResponseBody - public List getRules() throws ThingsboardException { - try { - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { - return checkNotNull(ruleService.findSystemRules()); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(ruleService.findAllTenantRulesByTenantId(tenantId)); - } - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/rule", params = {"limit"}, method = RequestMethod.GET) - @ResponseBody - public TextPageData getTenantRules( - @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { - try { - TenantId tenantId = getCurrentUser().getTenantId(); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(ruleService.findTenantRules(tenantId, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/rule/{ruleId}", method = RequestMethod.DELETE) - @ResponseStatus(value = HttpStatus.OK) - public void deleteRule(@PathVariable(RULE_ID) String strRuleId) throws ThingsboardException { - checkParameter(RULE_ID, strRuleId); - try { - RuleId ruleId = new RuleId(toUUID(strRuleId)); - RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId)); - ruleService.deleteRuleById(ruleId); - actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED); - - logEntityAction(ruleId, rule, - null, - ActionType.DELETED, null, strRuleId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.RULE), - null, - null, - ActionType.DELETED, e, strRuleId); - - throw handleException(e); - } - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginCallback.java b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java similarity index 70% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginCallback.java rename to application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java index 413e36becb..15ee5ec255 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginCallback.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java @@ -13,14 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins; +package org.thingsboard.server.controller; /** - * @author Andrew Shvayka + * Created by ashvayka on 17.05.18. */ -public interface PluginCallback { - - void onSuccess(PluginContext ctx, T value); - - void onFailure(PluginContext ctx, Exception e); +public class TbUrlConstants { + public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry"; + public static final String RPC_URL_PREFIX = "/api/plugins/rpc"; } diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java new file mode 100644 index 0000000000..c2839ff37a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -0,0 +1,570 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Function; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.StringUtils; +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; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvQuery; +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.telemetry.AttributeData; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; +import org.thingsboard.server.service.telemetry.TsData; +import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; +import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +/** + * Created by ashvayka on 22.03.18. + */ +@RestController +@RequestMapping(TbUrlConstants.TELEMETRY_URL_PREFIX) +@Slf4j +public class TelemetryController extends BaseController { + + @Autowired + private AttributesService attributesService; + + @Autowired + private TimeseriesService tsService; + + @Autowired + private TelemetrySubscriptionService tsSubService; + + @Autowired + private AccessValidator accessValidator; + + private ExecutorService executor; + + @PostConstruct + public void initExecutor() { + executor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getAttributeKeys( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, this::getAttributeKeysCallback); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getAttributeKeysByScope( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr + , @PathVariable("scope") String scope) throws ThingsboardException { + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> getAttributeKeysCallback(result, entityId, scope)); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getAttributes( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getAttributesByScope( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getTimeseriesKeys( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> { + Futures.addCallback(tsService.findAllLatest(entityId), getTsKeysToResponseCallback(result)); + }); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) + @ResponseBody + public DeferredResult getLatestTimeseries( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr)); + } + + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) + @ResponseBody + public DeferredResult getTimeseries( + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @RequestParam(name = "keys") String keys, + @RequestParam(name = "startTs") Long startTs, + @RequestParam(name = "endTs") Long endTs, + @RequestParam(name = "interval", defaultValue = "0") Long interval, + @RequestParam(name = "limit", defaultValue = "100") Integer limit, + @RequestParam(name = "agg", defaultValue = "NONE") String aggStr + ) throws ThingsboardException { + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityType, entityIdStr, + (result, entityId) -> { + // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted + Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); + List queries = toKeysList(keys).stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, interval, limit, agg)) + .collect(Collectors.toList()); + + Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result)); + }); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, + @RequestBody JsonNode request) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return saveAttributes(entityId, scope, request); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, + @RequestBody JsonNode request) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(entityId, scope, request); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/attributes/{scope}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, + @RequestBody JsonNode request) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveAttributes(entityId, scope, request); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, + @RequestBody String requestBody) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(entityId, requestBody, 0L); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, + @RequestBody String requestBody) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return saveTelemetry(entityId, requestBody, ttl); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) + @ResponseBody + public DeferredResult deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, + @PathVariable("scope") String scope, + @RequestParam(name = "keys") String keysStr) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); + return deleteAttributes(entityId, scope, keysStr); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE) + @ResponseBody + public DeferredResult deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, + @PathVariable("scope") String scope, + @RequestParam(name = "keys") String keysStr) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + return deleteAttributes(entityId, scope, keysStr); + } + + private DeferredResult deleteAttributes(EntityId entityIdStr, String scope, String keysStr) throws ThingsboardException { + List keys = toKeysList(keysStr); + if (keys.isEmpty()) { + return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); + } + SecurityUser user = getCurrentUser(); + if (DataConstants.SERVER_SCOPE.equals(scope) || + DataConstants.SHARED_SCOPE.equals(scope) || + DataConstants.CLIENT_SCOPE.equals(scope)) { + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdStr, (result, entityId) -> { + ListenableFuture> future = attributesService.removeAll(entityId, scope, keys); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List tmp) { + logAttributesDeleted(user, entityId, scope, keys, null); + result.setResult(new ResponseEntity<>(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logAttributesDeleted(user, entityId, scope, keys, t); + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); + } + }, executor); + }); + } else { + return getImmediateDeferredResult("Invalid attribute scope: " + scope, HttpStatus.BAD_REQUEST); + } + } + + private DeferredResult saveAttributes(EntityId entityIdSrc, String scope, JsonNode json) throws ThingsboardException { + if (!DataConstants.SERVER_SCOPE.equals(scope) && !DataConstants.SHARED_SCOPE.equals(scope)) { + return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST); + } + if (json.isObject()) { + List attributes = extractRequestAttributes(json); + if (attributes.isEmpty()) { + return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); + } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, entityId) -> { + tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + logAttributesUpdated(user, entityId, scope, attributes, null); + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + logAttributesUpdated(user, entityId, scope, attributes, t); + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }); + }); + } else { + return getImmediateDeferredResult("Request is not a JSON object", HttpStatus.BAD_REQUEST); + } + } + + private DeferredResult saveTelemetry(EntityId entityIdSrc, String requestBody, long ttl) throws ThingsboardException { + TelemetryUploadRequest telemetryRequest; + JsonElement telemetryJson; + try { + telemetryJson = new JsonParser().parse(requestBody); + } catch (Exception e) { + return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST); + } + try { + telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson); + } catch (Exception e) { + return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST); + } + List entries = new ArrayList<>(); + for (Map.Entry> entry : telemetryRequest.getData().entrySet()) { + for (KvEntry kv : entry.getValue()) { + entries.add(new BasicTsKvEntry(entry.getKey(), kv)); + } + } + if (entries.isEmpty()) { + return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST); + } + SecurityUser user = getCurrentUser(); + return accessValidator.validateEntityAndCallback(getCurrentUser(), entityIdSrc, (result, entityId) -> { + tsSubService.saveAndNotify(entityId, entries, ttl, new FutureCallback() { + @Override + public void onSuccess(@Nullable Void tmp) { + result.setResult(new ResponseEntity(HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable t) { + AccessValidator.handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR); + } + }); + }); + } + + private void getLatestTimeseriesValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, String keys) { + ListenableFuture> future; + if (StringUtils.isEmpty(keys)) { + future = tsService.findAllLatest(entityId); + } else { + future = tsService.findLatest(entityId, toKeysList(keys)); + } + Futures.addCallback(future, getTsKvListCallback(result)); + } + + private void getAttributeValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, String scope, String keys) { + List keyList = toKeysList(keys); + FutureCallback> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList); + if (!StringUtils.isEmpty(scope)) { + if (keyList != null && !keyList.isEmpty()) { + Futures.addCallback(attributesService.find(entityId, scope, keyList), callback); + } else { + Futures.addCallback(attributesService.findAll(entityId, scope), callback); + } + } else { + List>> futures = new ArrayList<>(); + for (String tmpScope : DataConstants.allScopes()) { + if (keyList != null && !keyList.isEmpty()) { + futures.add(attributesService.find(entityId, tmpScope, keyList)); + } else { + futures.add(attributesService.findAll(entityId, tmpScope)); + } + } + + ListenableFuture> future = mergeAllAttributesFutures(futures); + + Futures.addCallback(future, callback); + } + } + + private void getAttributeKeysCallback(@Nullable DeferredResult result, EntityId entityId, String scope) { + Futures.addCallback(attributesService.findAll(entityId, scope), getAttributeKeysToResponseCallback(result)); + } + + private void getAttributeKeysCallback(@Nullable DeferredResult result, EntityId entityId) { + List>> futures = new ArrayList<>(); + for (String scope : DataConstants.allScopes()) { + futures.add(attributesService.findAll(entityId, scope)); + } + + ListenableFuture> future = mergeAllAttributesFutures(futures); + + Futures.addCallback(future, getAttributeKeysToResponseCallback(result)); + } + + private FutureCallback> getTsKeysToResponseCallback(final DeferredResult response) { + return new FutureCallback>() { + @Override + public void onSuccess(List values) { + List keys = values.stream().map(KvEntry::getKey).collect(Collectors.toList()); + response.setResult(new ResponseEntity<>(keys, HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable e) { + log.error("Failed to fetch attributes", e); + AccessValidator.handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR); + } + }; + } + + private FutureCallback> getAttributeKeysToResponseCallback(final DeferredResult response) { + return new FutureCallback>() { + + @Override + public void onSuccess(List attributes) { + List keys = attributes.stream().map(KvEntry::getKey).collect(Collectors.toList()); + response.setResult(new ResponseEntity<>(keys, HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable e) { + log.error("Failed to fetch attributes", e); + AccessValidator.handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR); + } + }; + } + + private FutureCallback> getAttributeValuesToResponseCallback(final DeferredResult response, + final SecurityUser user, final String scope, + final EntityId entityId, final List keyList) { + return new FutureCallback>() { + @Override + public void onSuccess(List attributes) { + List values = attributes.stream().map(attribute -> new AttributeData(attribute.getLastUpdateTs(), + attribute.getKey(), attribute.getValue())).collect(Collectors.toList()); + logAttributesRead(user, entityId, scope, keyList, null); + response.setResult(new ResponseEntity<>(values, HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable e) { + log.error("Failed to fetch attributes", e); + logAttributesRead(user, entityId, scope, keyList, e); + AccessValidator.handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR); + } + }; + } + + private FutureCallback> getTsKvListCallback(final DeferredResult response) { + return new FutureCallback>() { + @Override + public void onSuccess(List data) { + Map> result = new LinkedHashMap<>(); + for (TsKvEntry entry : data) { + result.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()) + .add(new TsData(entry.getTs(), entry.getValueAsString())); + } + response.setResult(new ResponseEntity<>(result, HttpStatus.OK)); + } + + @Override + public void onFailure(Throwable e) { + log.error("Failed to fetch historical data", e); + AccessValidator.handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR); + } + }; + } + + private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) { + try { + logEntityAction(user, (UUIDBased & EntityId)entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), + scope, keys); + } catch (ThingsboardException te) { + log.warn("Failed to log attributes delete", te); + } + } + + private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) { + try { + logEntityAction(user, (UUIDBased & EntityId)entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e), + scope, attributes); + } catch (ThingsboardException te) { + log.warn("Failed to log attributes update", te); + } + } + + + private void logAttributesRead(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) { + try { + logEntityAction(user, (UUIDBased & EntityId)entityId, null, null, ActionType.ATTRIBUTES_READ, toException(e), + scope, keys); + } catch (ThingsboardException te) { + log.warn("Failed to log attributes read", te); + } + } + + private ListenableFuture> mergeAllAttributesFutures(List>> futures) { + return Futures.transform(Futures.successfulAsList(futures), + (Function>, ? extends List>) input -> { + List tmp = new ArrayList<>(); + if (input != null) { + input.forEach(tmp::addAll); + } + return tmp; + }, executor); + } + + private List toKeysList(String keys) { + List keyList = null; + if (!StringUtils.isEmpty(keys)) { + keyList = Arrays.asList(keys.split(",")); + } + return keyList; + } + + private DeferredResult getImmediateDeferredResult(String message, HttpStatus status) { + DeferredResult result = new DeferredResult<>(); + result.setResult(new ResponseEntity<>(message, status)); + return result; + } + + private List extractRequestAttributes(JsonNode jsonNode) { + long ts = System.currentTimeMillis(); + List attributes = new ArrayList<>(); + jsonNode.fields().forEachRemaining(entry -> { + String key = entry.getKey(); + JsonNode value = entry.getValue(); + if (entry.getValue().isTextual()) { + attributes.add(new BaseAttributeKvEntry(new StringDataEntry(key, value.textValue()), ts)); + } else if (entry.getValue().isBoolean()) { + attributes.add(new BaseAttributeKvEntry(new BooleanDataEntry(key, value.booleanValue()), ts)); + } else if (entry.getValue().isDouble()) { + attributes.add(new BaseAttributeKvEntry(new DoubleDataEntry(key, value.doubleValue()), ts)); + } else if (entry.getValue().isNumber()) { + if (entry.getValue().isBigInteger()) { + throw new UncheckedApiException(new InvalidParametersException("Big integer values are not supported!")); + } else { + attributes.add(new BaseAttributeKvEntry(new LongDataEntry(key, value.longValue()), ts)); + } + } + }); + return attributes; + } +} diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java index 5acb4eb216..1a7c116560 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java @@ -15,21 +15,34 @@ */ package org.thingsboard.server.controller; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.exception.ThingsboardException; +import org.thingsboard.server.service.install.InstallScripts; @RestController @RequestMapping("/api") +@Slf4j public class TenantController extends BaseController { - + + @Autowired + private InstallScripts installScripts; + @Autowired private TenantService tenantService; @@ -49,10 +62,15 @@ public class TenantController extends BaseController { @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenant", method = RequestMethod.POST) - @ResponseBody + @ResponseBody public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { try { - return checkNotNull(tenantService.saveTenant(tenant)); + boolean newTenant = tenant.getId() == null; + tenant = checkNotNull(tenantService.saveTenant(tenant)); + if (newTenant) { + installScripts.createDefaultRuleChains(tenant.getId()); + } + return tenant; } catch (Exception e) { throw handleException(e); } @@ -72,7 +90,7 @@ public class TenantController extends BaseController { } @PreAuthorize("hasAuthority('SYS_ADMIN')") - @RequestMapping(value = "/tenants", params = { "limit" }, method = RequestMethod.GET) + @RequestMapping(value = "/tenants", params = {"limit"}, method = RequestMethod.GET) @ResponseBody public TextPageData getTenants(@RequestParam int limit, @RequestParam(required = false) String textSearch, @@ -85,5 +103,5 @@ public class TenantController extends BaseController { throw handleException(e); } } - + } diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index 2a1531ac99..5f6c1cef21 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -18,10 +18,20 @@ package org.thingsboard.server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -29,9 +39,6 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; -import org.thingsboard.server.service.mail.MailService; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.http.HttpServletRequest; diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 44c7d94b3d..60f40a8f23 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -17,13 +17,20 @@ package org.thingsboard.server.controller; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardException; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 757f765dce..eb229fe7ed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -17,7 +17,15 @@ package org.thingsboard.server.controller; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; +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.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.TextPageData; @@ -25,7 +33,6 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.exception.ThingsboardException; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java deleted file mode 100644 index 8e3cee4d5d..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller.plugin; - - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.actors.service.ActorService; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.controller.BaseController; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.exception.ThingsboardException; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.PluginConstants; -import org.thingsboard.server.extensions.api.plugins.rest.BasicPluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.rest.RestRequest; - -import javax.servlet.http.HttpServletRequest; - -@RestController -@RequestMapping(PluginConstants.PLUGIN_URL_PREFIX) -@Slf4j -public class PluginApiController extends BaseController { - - @SuppressWarnings("rawtypes") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/{pluginToken}/**") - @ResponseStatus(value = HttpStatus.OK) - public DeferredResult processRequest( - @PathVariable("pluginToken") String pluginToken, - RequestEntity requestEntity, - HttpServletRequest request) - throws ThingsboardException { - log.debug("[{}] Going to process requst uri: {}", pluginToken, requestEntity.getUrl()); - DeferredResult result = new DeferredResult(); - PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken); - if (pluginMd == null) { - result.setErrorResult(new PluginNotFoundException("Plugin with token: " + pluginToken + " not found!")); - } else { - TenantId tenantId = getCurrentUser().getTenantId(); - CustomerId customerId = getCurrentUser().getCustomerId(); - if (validatePluginAccess(pluginMd, tenantId, customerId)) { - if(tenantId != null && ModelConstants.NULL_UUID.equals(tenantId.getId())){ - tenantId = null; - } - UserId userId = getCurrentUser().getId(); - String userName = getCurrentUser().getName(); - PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), - tenantId, customerId, userId, userName); - actorService.process(new BasicPluginRestMsg(securityCtx, new RestRequest(requestEntity, request), result)); - } else { - result.setResult(new ResponseEntity<>(HttpStatus.FORBIDDEN)); - } - - } - return result; - } - - public static boolean validatePluginAccess(PluginMetaData pluginMd, TenantId tenantId, CustomerId customerId) { - boolean systemAdministrator = tenantId == null || ModelConstants.NULL_UUID.equals(tenantId.getId()); - boolean tenantAdministrator = !systemAdministrator && (customerId == null || ModelConstants.NULL_UUID.equals(customerId.getId())); - boolean systemPlugin = ModelConstants.NULL_UUID.equals(pluginMd.getTenantId().getId()); - - boolean validUser = false; - if (systemPlugin) { - if (pluginMd.isPublicAccess() || systemAdministrator) { - // All users can access public system plugins. Only system - // users can access private system plugins - validUser = true; - } - } else { - if ((pluginMd.isPublicAccess() || tenantAdministrator) && tenantId != null && tenantId.equals(pluginMd.getTenantId())) { - // All tenant users can access public tenant plugins. Only tenant - // administrator can access private tenant plugins - validUser = true; - } - } - return validUser; - } -} diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java deleted file mode 100644 index 7fc12cb96e..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginNotFoundException.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller.plugin; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(HttpStatus.NOT_FOUND) -public class PluginNotFoundException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public PluginNotFoundException(String message){ - super(message); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java deleted file mode 100644 index 5e04ac266d..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketMsgEndpoint.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller.plugin; - -import java.io.IOException; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; - -public interface PluginWebSocketMsgEndpoint { - - void send(PluginWebsocketMsg wsMsg) throws IOException; - - void close(PluginWebsocketSessionRef sessionRef) throws IOException; -} diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java similarity index 62% rename from application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java rename to application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 7f835ed5bf..b771ee2c25 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -15,57 +15,39 @@ */ package org.thingsboard.server.controller.plugin; -import java.io.IOException; -import java.net.URI; -import java.security.InvalidParameterException; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanCreationNotAllowedException; -import org.springframework.context.annotation.Lazy; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.actors.service.ActorService; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.config.WebSocketConfiguration; -import org.thingsboard.server.extensions.api.plugins.PluginConstants; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.ws.BasicPluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.SessionEventPluginWebSocketMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.TextPluginWebSocketMsg; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.thingsboard.server.config.WebSocketConfiguration; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.telemetry.SessionEvent; +import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; + +import java.io.IOException; +import java.net.URI; +import java.security.InvalidParameterException; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; @Service @Slf4j -public class PluginWebSocketHandler extends TextWebSocketHandler implements PluginWebSocketMsgEndpoint { +public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { private static final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); private static final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); @Autowired @Lazy - private ActorService actorService; - - @Autowired - @Lazy - private PluginService pluginService; + private TelemetryWebSocketService webSocketService; @Override public void handleTextMessage(WebSocketSession session, TextMessage message) { @@ -73,7 +55,7 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug log.info("[{}] Processing {}", session.getId(), message); SessionMetaData sessionMd = internalSessionMap.get(session.getId()); if (sessionMd != null) { - actorService.process(new TextPluginWebSocketMsg(sessionMd.sessionRef, message.getPayload())); + webSocketService.handleWebSocketMsg(sessionMd.sessionRef, message.getPayload()); } else { log.warn("[{}] Failed to find session", session.getId()); session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!")); @@ -88,11 +70,11 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug super.afterConnectionEstablished(session); try { String internalSessionId = session.getId(); - PluginWebsocketSessionRef sessionRef = toRef(session); + TelemetryWebSocketSessionRef sessionRef = toRef(session); String externalSessionId = sessionRef.getSessionId(); internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef)); externalSessionMap.put(externalSessionId, internalSessionId); - actorService.process(new SessionEventPluginWebSocketMsg(sessionRef, SessionEvent.onEstablished())); + processInWebSocketService(sessionRef, SessionEvent.onEstablished()); log.info("[{}][{}] Session is started", externalSessionId, session.getId()); } catch (InvalidParameterException e) { log.warn("[[{}] Failed to start session", session.getId(), e); @@ -108,7 +90,7 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug super.handleTransportError(session, tError); SessionMetaData sessionMd = internalSessionMap.get(session.getId()); if (sessionMd != null) { - processInActorService(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onError(tError))); + processInWebSocketService(sessionMd.sessionRef, SessionEvent.onError(tError)); } else { log.warn("[{}] Failed to find session", session.getId()); } @@ -121,20 +103,20 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug SessionMetaData sessionMd = internalSessionMap.remove(session.getId()); if (sessionMd != null) { externalSessionMap.remove(sessionMd.sessionRef.getSessionId()); - processInActorService(new SessionEventPluginWebSocketMsg(sessionMd.sessionRef, SessionEvent.onClosed())); + processInWebSocketService(sessionMd.sessionRef, SessionEvent.onClosed()); } log.info("[{}] Session is closed", session.getId()); } - private void processInActorService(SessionEventPluginWebSocketMsg msg) { + private void processInWebSocketService(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { try { - actorService.process(msg); + webSocketService.handleWebSocketSessionEvent(sessionRef, event); } catch (BeanCreationNotAllowedException e) { - log.warn("[{}] Failed to close session due to possible shutdown state", msg.getSessionRef().getSessionId()); + log.warn("[{}] Failed to close session due to possible shutdown state", sessionRef.getSessionId()); } } - private PluginWebsocketSessionRef toRef(WebSocketSession session) throws IOException { + private TelemetryWebSocketSessionRef toRef(WebSocketSession session) throws IOException { URI sessionUri = session.getUri(); String path = sessionUri.getPath(); path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length()); @@ -142,33 +124,20 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug throw new IllegalArgumentException("URL should contain plugin token!"); } String[] pathElements = path.split("/"); - String pluginToken = pathElements[0]; - // TODO: cache - PluginMetaData pluginMd = pluginService.findPluginByApiToken(pluginToken); - if (pluginMd == null) { + String serviceToken = pathElements[0]; + if (!"telemetry".equalsIgnoreCase(serviceToken)) { throw new InvalidParameterException("Can't find plugin with specified token!"); } else { SecurityUser currentUser = (SecurityUser) session.getAttributes().get(WebSocketConfiguration.WS_SECURITY_USER_ATTRIBUTE); - TenantId tenantId = currentUser.getTenantId(); - CustomerId customerId = currentUser.getCustomerId(); - if (PluginApiController.validatePluginAccess(pluginMd, tenantId, customerId)) { - UserId userId = currentUser.getId(); - String userName = currentUser.getName(); - PluginApiCallSecurityContext securityCtx = new PluginApiCallSecurityContext(pluginMd.getTenantId(), pluginMd.getId(), tenantId, - currentUser.getCustomerId(), userId, userName); - return new BasicPluginWebsocketSessionRef(UUID.randomUUID().toString(), securityCtx, session.getUri(), session.getAttributes(), - session.getLocalAddress(), session.getRemoteAddress()); - } else { - throw new SecurityException("Current user is not allowed to use this plugin!"); - } + return new TelemetryWebSocketSessionRef(UUID.randomUUID().toString(), currentUser, session.getLocalAddress(), session.getRemoteAddress()); } } private static class SessionMetaData { private final WebSocketSession session; - private final PluginWebsocketSessionRef sessionRef; + private final TelemetryWebSocketSessionRef sessionRef; - public SessionMetaData(WebSocketSession session, PluginWebsocketSessionRef sessionRef) { + public SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) { super(); this.session = session; this.sessionRef = sessionRef; @@ -176,17 +145,15 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug } @Override - public void send(PluginWebsocketMsg wsMsg) throws IOException { - PluginWebsocketSessionRef sessionRef = wsMsg.getSessionRef(); + public void send(TelemetryWebSocketSessionRef sessionRef, String msg) throws IOException { String externalId = sessionRef.getSessionId(); - log.debug("[{}] Processing {}", externalId, wsMsg); + log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); if (internalId != null) { SessionMetaData sessionMd = internalSessionMap.get(internalId); if (sessionMd != null) { - if (wsMsg instanceof TextPluginWebSocketMsg) { - String payload = ((TextPluginWebSocketMsg) wsMsg).getPayload(); - sessionMd.session.sendMessage(new TextMessage(payload)); + synchronized (sessionMd) { + sessionMd.session.sendMessage(new TextMessage(msg)); } } else { log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId); @@ -197,7 +164,7 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug } @Override - public void close(PluginWebsocketSessionRef sessionRef) throws IOException { + public void close(TelemetryWebSocketSessionRef sessionRef) throws IOException { String externalId = sessionRef.getSessionId(); log.debug("[{}] Processing close request", externalId); String internalId = externalSessionMap.get(externalId); diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java index 3b897d6bb3..031073fa97 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java @@ -16,6 +16,7 @@ package org.thingsboard.server.exception; import org.springframework.http.HttpStatus; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import java.util.Date; diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index c70c5612c3..63fe17ac4f 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -17,8 +17,6 @@ package org.thingsboard.server.exception; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -27,6 +25,8 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index e765c40c86..f863d0bb2a 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -23,12 +23,11 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.service.component.ComponentDiscoveryService; +import org.thingsboard.server.service.install.DataUpdateService; import org.thingsboard.server.service.install.DatabaseSchemaService; import org.thingsboard.server.service.install.DatabaseUpgradeService; import org.thingsboard.server.service.install.SystemDataLoaderService; -import java.nio.file.Paths; - @Service @Profile("install") @Slf4j @@ -40,9 +39,6 @@ public class ThingsboardInstallService { @Value("${install.upgrade.from_version:1.2.3}") private String upgradeFromVersion; - @Value("${install.data_dir}") - private String dataDir; - @Value("${install.load_demo:false}") private Boolean loadDemo; @@ -61,6 +57,9 @@ public class ThingsboardInstallService { @Autowired private SystemDataLoaderService systemDataLoaderService; + @Autowired + private DataUpdateService dataUpdateService; + public void performInstall() { try { if (isUpgrade) { @@ -77,11 +76,18 @@ public class ThingsboardInstallService { databaseUpgradeService.upgradeDatabase("1.3.0"); - case "1.3.1": + case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ..."); databaseUpgradeService.upgradeDatabase("1.3.1"); + case "1.4.0": + log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ..."); + + databaseUpgradeService.upgradeDatabase("1.4.0"); + + dataUpdateService.updateData("1.4.0"); + log.info("Updating system data..."); systemDataLoaderService.deleteSystemWidgetBundle("charts"); @@ -108,13 +114,6 @@ public class ThingsboardInstallService { log.info("Starting ThingsBoard Installation..."); - if (this.dataDir == null) { - throw new RuntimeException("'install.data_dir' property should specified!"); - } - if (!Paths.get(this.dataDir).toFile().isDirectory()) { - throw new RuntimeException("'install.data_dir' property value is not a valid directory!"); - } - log.info("Installing DataBase schema..."); databaseSchemaService.createDatabaseSchema(); @@ -126,8 +125,8 @@ public class ThingsboardInstallService { systemDataLoaderService.createSysAdmin(); systemDataLoaderService.createAdminSettings(); systemDataLoaderService.loadSystemWidgets(); - systemDataLoaderService.loadSystemPlugins(); - systemDataLoaderService.loadSystemRules(); +// systemDataLoaderService.loadSystemPlugins(); +// systemDataLoaderService.loadSystemRules(); if (loadDemo) { log.info("Loading demo data..."); diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java index 865325fe27..c21f1aa84c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/DummyDiscoveryService.java @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; -import org.thingsboard.server.service.environment.EnvironmentLogService; import javax.annotation.PostConstruct; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java index 03c9694865..6eee5f3061 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ServerInstance.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.service.cluster.discovery; -import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo; +import org.thingsboard.server.gen.discovery.ServerInstanceProtos; /** * @author Andrew Shvayka @@ -29,8 +28,6 @@ import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo; @EqualsAndHashCode(exclude = {"serverInfo", "serverAddress"}) public final class ServerInstance implements Comparable { - @Getter(AccessLevel.PACKAGE) - private final ServerInfo serverInfo; @Getter private final String host; @Getter @@ -38,8 +35,13 @@ public final class ServerInstance implements Comparable { @Getter private final ServerAddress serverAddress; - public ServerInstance(ServerInfo serverInfo) { - this.serverInfo = serverInfo; + public ServerInstance(ServerAddress serverAddress) { + this.serverAddress = serverAddress; + this.host = serverAddress.getHost(); + this.port = serverAddress.getPort(); + } + + public ServerInstance(ServerInstanceProtos.ServerInfo serverInfo) { this.host = serverInfo.getHost(); this.port = serverInfo.getPort(); this.serverAddress = new ServerAddress(host, port); diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java index 818d2b1c06..6002b0e67e 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java @@ -15,8 +15,9 @@ */ package org.thingsboard.server.service.cluster.discovery; -import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.SerializationException; +import org.apache.commons.lang3.SerializationUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.ChildData; @@ -31,15 +32,17 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.Assert; -import org.thingsboard.server.gen.discovery.ServerInstanceProtos.ServerInfo; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.utils.MiscUtils; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.io.IOException; import java.util.List; +import java.util.NoSuchElementException; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -67,6 +70,10 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @Autowired private ServerInstanceService serverInstance; + @Autowired + @Lazy + private TelemetrySubscriptionService tsSubService; + private final List listeners = new CopyOnWriteArrayList<>(); private CuratorFramework client; @@ -113,7 +120,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi log.info("[{}:{}] Creating ZK node for current instance", self.getHost(), self.getPort()); nodePath = client.create() .creatingParentsIfNeeded() - .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", self.getServerInfo().toByteArray()); + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(zkNodesDir + "/", SerializationUtils.serialize(self.getServerAddress())); log.info("[{}:{}] Created ZK node for current instance: {}", self.getHost(), self.getPort(), nodePath); } catch (Exception e) { log.error("Failed to create ZK node", e); @@ -144,8 +151,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi .filter(cd -> !cd.getPath().equals(nodePath)) .map(cd -> { try { - return new ServerInstance(ServerInfo.parseFrom(cd.getData())); - } catch (InvalidProtocolBufferException e) { + return new ServerInstance( (ServerAddress) SerializationUtils.deserialize(cd.getData())); + } catch (NoSuchElementException e) { log.error("Failed to decode ZK node", e); throw new RuntimeException(e); } @@ -186,20 +193,23 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi } ServerInstance instance; try { - instance = new ServerInstance(ServerInfo.parseFrom(data.getData())); - } catch (IOException e) { + ServerAddress serverAddress = SerializationUtils.deserialize(data.getData()); + instance = new ServerInstance(serverAddress); + } catch (SerializationException e) { log.error("Failed to decode server instance for node {}", data.getPath(), e); throw e; } log.info("Processing [{}] event for [{}:{}]", pathChildrenCacheEvent.getType(), instance.getHost(), instance.getPort()); switch (pathChildrenCacheEvent.getType()) { case CHILD_ADDED: + tsSubService.onClusterUpdate(); listeners.forEach(listener -> listener.onServerAdded(instance)); break; case CHILD_UPDATED: listeners.forEach(listener -> listener.onServerUpdated(instance)); break; case CHILD_REMOVED: + tsSubService.onClusterUpdate(); listeners.forEach(listener -> listener.onServerRemoved(instance)); break; default: diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java index b29668c36a..272073dd23 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ClusterRoutingService.java @@ -16,9 +16,7 @@ package org.thingsboard.server.service.cluster.routing; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.service.cluster.discovery.ServerInstance; import java.util.Optional; import java.util.UUID; diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java index 40677976eb..5087b5ce46 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/routing/ConsistentClusterRoutingService.java @@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener; @@ -107,7 +106,7 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D @Override public void onServerAdded(ServerInstance server) { - log.debug("On server added event: {}", server); + log.info("On server added event: {}", server); addNode(server); logCircle(); } @@ -119,7 +118,7 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D @Override public void onServerRemoved(ServerInstance server) { - log.debug("On server removed event: {}", server); + log.info("On server removed event: {}", server); removeNode(server); logCircle(); } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java index 5a6b30735e..19715b826f 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterGrpcService.java @@ -19,38 +19,24 @@ import com.google.protobuf.ByteString; import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.SerializationUtils; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; -import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; -import org.thingsboard.server.actors.service.ActorService; -import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; -import org.thingsboard.server.service.cluster.discovery.DiscoveryService; import org.thingsboard.server.service.cluster.discovery.ServerInstance; import org.thingsboard.server.service.cluster.discovery.ServerInstanceService; -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; +import org.thingsboard.server.service.encoding.DataDecodingEncodingService; -import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; -import java.util.Set; import java.util.UUID; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -64,13 +50,17 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @Autowired private ServerInstanceService instanceService; + @Autowired + private DataDecodingEncodingService encodingService; + private RpcMsgListener listener; private Server server; private ServerInstance instance; - private ConcurrentMap pendingSessionMap = new ConcurrentHashMap<>(); + private ConcurrentMap>> pendingSessionMap = + new ConcurrentHashMap<>(); public void init(RpcMsgListener listener) { this.listener = listener; @@ -88,11 +78,11 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI } @Override - public void onSessionCreated(UUID msgUid, StreamObserver msg) { - RpcSessionCreationFuture future = pendingSessionMap.remove(msgUid); - if (future != null) { + public void onSessionCreated(UUID msgUid, StreamObserver inputStream) { + BlockingQueue> queue = pendingSessionMap.remove(msgUid); + if (queue != null) { try { - future.onMsg(msg); + queue.put(inputStream); } catch (InterruptedException e) { log.warn("Failed to report created session!"); Thread.currentThread().interrupt(); @@ -103,11 +93,13 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI } @Override - public StreamObserver handlePluginMsgs(StreamObserver responseObserver) { + public StreamObserver handleMsgs( + StreamObserver responseObserver) { log.info("Processing new session."); return createSession(new RpcSessionCreateRequestMsg(UUID.randomUUID(), null, responseObserver)); } + @PreDestroy public void stop() { if (server != null) { @@ -123,65 +115,18 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI } } - @Override - public void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToDeviceActorRpcMsg(toProtoMsg(toForward)).build(); - tell(serverAddress, msg); - } - - @Override - public void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToDeviceActorNotificationRpcMsg(toProtoMsg(toForward)).build(); - tell(serverAddress, msg); - } - - @Override - public void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build(); - tell(serverAddress, msg); - } @Override - public void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToPluginRpcResponseRpcMsg(toProtoMsg(toForward)).build(); - tell(serverAddress, msg); + public void broadcast(RpcBroadcastMsg msg) { + listener.onBroadcastMsg(msg); } - @Override - public void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToDeviceSessionActorRpcMsg(toProtoMsg(toForward)).build(); - tell(serverAddress, msg); - } - - @Override - public void tell(PluginRpcMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToPluginRpcMsg(toProtoMsg(toForward)).build(); - tell(toForward.getRpcMsg().getServerAddress(), msg); - } - - @Override - public void broadcast(ToAllNodesMsg toForward) { - ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() - .setToAllNodesRpcMsg(toProtoMsg(toForward)).build(); - listener.onMsg(new RpcBroadcastMsg(msg)); - } - - private void tell(ServerAddress serverAddress, ClusterAPIProtos.ToRpcServerMessage msg) { - listener.onMsg(new RpcSessionTellMsg(serverAddress, msg)); - } - - private StreamObserver createSession(RpcSessionCreateRequestMsg msg) { - RpcSessionCreationFuture future = new RpcSessionCreationFuture(); - pendingSessionMap.put(msg.getMsgUid(), future); - listener.onMsg(msg); + private StreamObserver createSession(RpcSessionCreateRequestMsg msg) { + BlockingQueue> queue = new ArrayBlockingQueue<>(1); + pendingSessionMap.put(msg.getMsgUid(), queue); + listener.onRpcSessionCreateRequestMsg(msg); try { - StreamObserver observer = future.get(); + StreamObserver observer = queue.take(); log.info("Processed new session."); return observer; } catch (Exception e) { @@ -190,86 +135,27 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI } } - private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(ToDeviceActorMsg msg) { - return ClusterAPIProtos.ToDeviceActorRpcMessage.newBuilder().setData( - ByteString.copyFrom(SerializationUtils.serialize(msg)) - ).build(); - } - - private static ClusterAPIProtos.ToDeviceActorNotificationRpcMessage toProtoMsg(ToDeviceActorNotificationMsg msg) { - return ClusterAPIProtos.ToDeviceActorNotificationRpcMessage.newBuilder().setData( - ByteString.copyFrom(SerializationUtils.serialize(msg)) - ).build(); - } - - private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestPluginMsg msg) { - ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder(); - ToDeviceRpcRequest request = msg.getMsg(); - - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder() - .setTenantId(toUid(msg.getPluginTenantId().getId())) - .setPluginId(toUid(msg.getPluginId().getId())) - .build()); - - builder.setDeviceTenantId(toUid(msg.getTenantId())); - builder.setDeviceId(toUid(msg.getDeviceId())); - - builder.setMsgId(toUid(request.getId())); - builder.setOneway(request.isOneway()); - builder.setExpTime(request.getExpirationTime()); - builder.setMethod(request.getBody().getMethod()); - builder.setParams(request.getBody().getParams()); - - return builder.build(); - } - - private static ClusterAPIProtos.ToPluginRpcResponseRpcMessage toProtoMsg(ToPluginRpcResponseDeviceMsg msg) { - ClusterAPIProtos.ToPluginRpcResponseRpcMessage.Builder builder = ClusterAPIProtos.ToPluginRpcResponseRpcMessage.newBuilder(); - FromDeviceRpcResponse request = msg.getResponse(); - - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder() - .setTenantId(toUid(msg.getPluginTenantId().getId())) - .setPluginId(toUid(msg.getPluginId().getId())) - .build()); - - builder.setMsgId(toUid(request.getId())); - request.getResponse().ifPresent(builder::setResponse); - request.getError().ifPresent(e -> builder.setError(e.name())); - - return builder.build(); - } - - private ClusterAPIProtos.ToAllNodesRpcMessage toProtoMsg(ToAllNodesMsg msg) { - return ClusterAPIProtos.ToAllNodesRpcMessage.newBuilder().setData( - ByteString.copyFrom(SerializationUtils.serialize(msg)) - ).build(); - } - - - private ClusterAPIProtos.ToPluginRpcMessage toProtoMsg(PluginRpcMsg msg) { - return ClusterAPIProtos.ToPluginRpcMessage.newBuilder() - .setClazz(msg.getRpcMsg().getMsgClazz()) - .setData(ByteString.copyFrom(msg.getRpcMsg().getMsgData())) - .setAddress(ClusterAPIProtos.PluginAddress.newBuilder() - .setTenantId(toUid(msg.getPluginTenantId().getId())) - .setPluginId(toUid(msg.getPluginId().getId())) - .build() - ).build(); - } - - private static ClusterAPIProtos.Uid toUid(EntityId uuid) { - return toUid(uuid.getId()); + @Override + public void tell(ClusterAPIProtos.ClusterMessage message) { + listener.onSendMsg(message); } - private static ClusterAPIProtos.Uid toUid(UUID uuid) { - return ClusterAPIProtos.Uid.newBuilder().setPluginUuidMsb(uuid.getMostSignificantBits()).setPluginUuidLsb( - uuid.getLeastSignificantBits()).build(); + @Override + public void tell(ServerAddress serverAddress, TbActorMsg actorMsg) { + listener.onSendMsg(encodingService.convertToProtoDataMessage(serverAddress, actorMsg)); } - private static ClusterAPIProtos.ToDeviceSessionActorRpcMessage toProtoMsg(ToDeviceSessionActorMsg msg) { - return ClusterAPIProtos.ToDeviceSessionActorRpcMessage.newBuilder().setData( - ByteString.copyFrom(SerializationUtils.serialize(msg)) - ).build(); + @Override + public void tell(ServerAddress serverAddress, ClusterAPIProtos.MessageType msgType, byte[] data) { + ClusterAPIProtos.ClusterMessage msg = ClusterAPIProtos.ClusterMessage + .newBuilder() + .setServerAddress(ClusterAPIProtos.ServerAddress + .newBuilder() + .setHost(serverAddress.getHost()) + .setPort(serverAddress.getPort()) + .build()) + .setMessageType(msgType) + .setPayload(ByteString.copyFrom(data)).build(); + listener.onSendMsg(msg); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java index 8c50bb7207..de29b89df2 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/ClusterRpcService.java @@ -16,14 +16,9 @@ package org.thingsboard.server.service.cluster.rpc; import io.grpc.stub.StreamObserver; +import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import java.util.UUID; @@ -35,19 +30,13 @@ public interface ClusterRpcService { void init(RpcMsgListener listener); - void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward); + void broadcast(RpcBroadcastMsg msg); - void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward); + void onSessionCreated(UUID msgUid, StreamObserver inputStream); - void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward); + void tell(ClusterAPIProtos.ClusterMessage message); - void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward); + void tell(ServerAddress serverAddress, TbActorMsg actorMsg); - void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward); - - void tell(PluginRpcMsg toForward); - - void broadcast(ToAllNodesMsg msg); - - void onSessionCreated(UUID msgUid, StreamObserver inputStream); + void tell(ServerAddress serverAddress, ClusterAPIProtos.MessageType msgType, byte[] data); } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java index c4038954e4..7216c4319f 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSession.java @@ -33,8 +33,8 @@ public final class GrpcSession implements Closeable { private final UUID sessionId; private final boolean client; private final GrpcSessionListener listener; - private StreamObserver inputStream; - private StreamObserver outputStream; + private StreamObserver inputStream; + private StreamObserver outputStream; private boolean connected; private ServerAddress remoteServer; @@ -56,17 +56,17 @@ public final class GrpcSession implements Closeable { } public void initInputStream() { - this.inputStream = new StreamObserver() { + this.inputStream = new StreamObserver() { @Override - public void onNext(ClusterAPIProtos.ToRpcServerMessage msg) { - if (!connected && msg.hasConnectMsg()) { + public void onNext(ClusterAPIProtos.ClusterMessage clusterMessage) { + if (!connected && clusterMessage.getMessageType() == ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE) { connected = true; - ClusterAPIProtos.ServerAddress rpcAddress = msg.getConnectMsg().getServerAddress(); + ServerAddress rpcAddress = new ServerAddress(clusterMessage.getServerAddress().getHost(), clusterMessage.getServerAddress().getPort()); remoteServer = new ServerAddress(rpcAddress.getHost(), rpcAddress.getPort()); listener.onConnected(GrpcSession.this); } if (connected) { - handleToRpcServerMessage(msg); + listener.onReceiveClusterGrpcMsg(GrpcSession.this, clusterMessage); } } @@ -83,37 +83,13 @@ public final class GrpcSession implements Closeable { }; } - private void handleToRpcServerMessage(ClusterAPIProtos.ToRpcServerMessage msg) { - if (msg.hasToPluginRpcMsg()) { - listener.onToPluginRpcMsg(GrpcSession.this, msg.getToPluginRpcMsg()); - } - if (msg.hasToDeviceActorRpcMsg()) { - listener.onToDeviceActorRpcMsg(GrpcSession.this, msg.getToDeviceActorRpcMsg()); - } - if (msg.hasToDeviceSessionActorRpcMsg()) { - listener.onToDeviceSessionActorRpcMsg(GrpcSession.this, msg.getToDeviceSessionActorRpcMsg()); - } - if (msg.hasToDeviceActorNotificationRpcMsg()) { - listener.onToDeviceActorNotificationRpcMsg(GrpcSession.this, msg.getToDeviceActorNotificationRpcMsg()); - } - if (msg.hasToDeviceRpcRequestRpcMsg()) { - listener.onToDeviceRpcRequestRpcMsg(GrpcSession.this, msg.getToDeviceRpcRequestRpcMsg()); - } - if (msg.hasToPluginRpcResponseRpcMsg()) { - listener.onFromDeviceRpcResponseRpcMsg(GrpcSession.this, msg.getToPluginRpcResponseRpcMsg()); - } - if (msg.hasToAllNodesRpcMsg()) { - listener.onToAllNodesRpcMessage(GrpcSession.this, msg.getToAllNodesRpcMsg()); - } - } - public void initOutputStream() { if (client) { listener.onConnected(GrpcSession.this); } } - public void sendMsg(ClusterAPIProtos.ToRpcServerMessage msg) { + public void sendMsg(ClusterAPIProtos.ClusterMessage msg) { outputStream.onNext(msg); } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java index 44e06930f3..266b1f5ac9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/GrpcSessionListener.java @@ -26,20 +26,7 @@ public interface GrpcSessionListener { void onDisconnected(GrpcSession session); - void onToPluginRpcMsg(GrpcSession session, ClusterAPIProtos.ToPluginRpcMessage msg); - - void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg); - - void onToDeviceActorNotificationRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToDeviceActorNotificationRpcMessage msg); - - void onToDeviceSessionActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceSessionActorRpcMessage msg); - - void onToAllNodesRpcMessage(GrpcSession grpcSession, ClusterAPIProtos.ToAllNodesRpcMessage toAllNodesRpcMessage); - - void onToDeviceRpcRequestRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toDeviceRpcRequestRpcMsg); - - void onFromDeviceRpcResponseRpcMsg(GrpcSession grpcSession, ClusterAPIProtos.ToPluginRpcResponseRpcMessage toPluginRpcResponseRpcMsg); + void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage); void onError(GrpcSession session, Throwable t); - } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java index a5c31513be..33f3847ddc 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcMsgListener.java @@ -17,34 +17,16 @@ package org.thingsboard.server.service.cluster.rpc; import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; -import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; -import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; -import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.gen.cluster.ClusterAPIProtos; /** * @author Andrew Shvayka */ -public interface RpcMsgListener { - - void onMsg(ToDeviceActorMsg msg); - - void onMsg(ToDeviceActorNotificationMsg msg); - - void onMsg(ToDeviceSessionActorMsg msg); - - void onMsg(ToAllNodesMsg nodeMsg); - - void onMsg(ToPluginActorMsg msg); - - void onMsg(RpcSessionCreateRequestMsg msg); - - void onMsg(RpcSessionTellMsg rpcSessionTellMsg); - - void onMsg(RpcBroadcastMsg rpcBroadcastMsg); +public interface RpcMsgListener { + void onReceivedMsg(ServerAddress remoteServer, ClusterAPIProtos.ClusterMessage msg); + void onSendMsg(ClusterAPIProtos.ClusterMessage msg); + void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg); + void onBroadcastMsg(RpcBroadcastMsg msg); } diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcSessionCreationFuture.java b/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcSessionCreationFuture.java deleted file mode 100644 index f98a456c68..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/cluster/rpc/RpcSessionCreationFuture.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2018 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.cluster.rpc; - -import io.grpc.stub.StreamObserver; -import org.thingsboard.server.gen.cluster.ClusterAPIProtos; - -import java.util.concurrent.*; - -/** - * @author Andrew Shvayka - */ -public class RpcSessionCreationFuture implements Future> { - - private final BlockingQueue> queue = new ArrayBlockingQueue<>(1); - - public void onMsg(StreamObserver result) throws InterruptedException { - queue.put(result); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return false; - } - - @Override - public StreamObserver get() throws InterruptedException, ExecutionException { - return this.queue.take(); - } - - @Override - public StreamObserver get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - StreamObserver result = this.queue.poll(timeout, unit); - if (result == null) { - throw new TimeoutException(); - } else { - return result; - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 0a6081d8da..f3ceed2069 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.service.component; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Charsets; -import com.google.common.io.Resources; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -26,15 +26,27 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen import org.springframework.core.env.Environment; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.api.NodeDefinition; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.dao.component.ComponentDescriptorService; -import org.thingsboard.server.extensions.api.component.*; import javax.annotation.PostConstruct; import java.lang.annotation.Annotation; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; @Service @Slf4j @@ -66,6 +78,24 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe } } + private void registerRuleNodeComponents() { + Set ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class); + for (BeanDefinition def : ruleNodeBeanDefinitions) { + try { + String clazzName = def.getBeanClassName(); + Class clazz = Class.forName(clazzName); + RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); + ComponentType type = ruleNodeAnnotation.type(); + ComponentDescriptor component = scanAndPersistComponent(def, type); + components.put(component.getClazz(), component); + componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); + } catch (Exception e) { + log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); + throw new RuntimeException(e); + } + } + } + private void registerComponents(ComponentType type, Class annotation) { List components = persist(getBeanDefinitions(annotation), type); componentsMap.put(type, components); @@ -79,8 +109,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe private List persist(Set filterDefs, ComponentType type) { List result = new ArrayList<>(); for (BeanDefinition def : filterDefs) { - ComponentDescriptor scannedComponent = scanAndPersistComponent(def, type); - result.add(scannedComponent); + result.add(scanAndPersistComponent(def, type)); } return result; } @@ -91,49 +120,24 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe try { scannedComponent.setType(type); Class clazz = Class.forName(clazzName); - String descriptorResourceName; switch (type) { + case ENRICHMENT: case FILTER: - Filter filterAnnotation = clazz.getAnnotation(Filter.class); - scannedComponent.setName(filterAnnotation.name()); - scannedComponent.setScope(filterAnnotation.scope()); - descriptorResourceName = filterAnnotation.descriptor(); - break; - case PROCESSOR: - Processor processorAnnotation = clazz.getAnnotation(Processor.class); - scannedComponent.setName(processorAnnotation.name()); - scannedComponent.setScope(processorAnnotation.scope()); - descriptorResourceName = processorAnnotation.descriptor(); - break; + case TRANSFORMATION: case ACTION: - Action actionAnnotation = clazz.getAnnotation(Action.class); - scannedComponent.setName(actionAnnotation.name()); - scannedComponent.setScope(actionAnnotation.scope()); - descriptorResourceName = actionAnnotation.descriptor(); - break; - case PLUGIN: - Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class); - scannedComponent.setName(pluginAnnotation.name()); - scannedComponent.setScope(pluginAnnotation.scope()); - descriptorResourceName = pluginAnnotation.descriptor(); - for (Class actionClazz : pluginAnnotation.actions()) { - ComponentDescriptor actionComponent = getComponent(actionClazz.getName()) - .orElseThrow(() -> { - log.error("Can't initialize plugin {}, due to missing action {}!", def.getBeanClassName(), actionClazz.getName()); - return new ClassNotFoundException("Action: " + actionClazz.getName() + "is missing!"); - }); - if (actionComponent.getType() != ComponentType.ACTION) { - log.error("Plugin {} action {} has wrong component type!", def.getBeanClassName(), actionClazz.getName(), actionComponent.getType()); - throw new RuntimeException("Plugin " + def.getBeanClassName() + "action " + actionClazz.getName() + " has wrong component type!"); - } - } - scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(action -> action.getName()).collect(Collectors.joining(","))); + case EXTERNAL: + RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); + scannedComponent.setName(ruleNodeAnnotation.name()); + scannedComponent.setScope(ruleNodeAnnotation.scope()); + NodeDefinition nodeDefinition = prepareNodeDefinition(ruleNodeAnnotation); + ObjectNode configurationDescriptor = mapper.createObjectNode(); + JsonNode node = mapper.valueToTree(nodeDefinition); + configurationDescriptor.set("nodeDefinition", node); + scannedComponent.setConfigurationDescriptor(configurationDescriptor); break; default: throw new RuntimeException(type + " is not supported yet!"); } - scannedComponent.setConfigurationDescriptor(mapper.readTree( - Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8))); scannedComponent.setClazz(clazzName); log.info("Processing scanned component: {}", scannedComponent); } catch (Exception e) { @@ -156,6 +160,34 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe return scannedComponent; } + private NodeDefinition prepareNodeDefinition(RuleNode nodeAnnotation) throws Exception { + NodeDefinition nodeDefinition = new NodeDefinition(); + nodeDefinition.setDetails(nodeAnnotation.nodeDetails()); + nodeDefinition.setDescription(nodeAnnotation.nodeDescription()); + nodeDefinition.setInEnabled(nodeAnnotation.inEnabled()); + nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled()); + nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); + nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); + Class configClazz = nodeAnnotation.configClazz(); + NodeConfiguration config = configClazz.newInstance(); + NodeConfiguration defaultConfiguration = config.defaultConfiguration(); + nodeDefinition.setDefaultConfiguration(mapper.valueToTree(defaultConfiguration)); + nodeDefinition.setUiResources(nodeAnnotation.uiResources()); + nodeDefinition.setConfigDirective(nodeAnnotation.configDirective()); + nodeDefinition.setIcon(nodeAnnotation.icon()); + nodeDefinition.setIconUrl(nodeAnnotation.iconUrl()); + nodeDefinition.setDocUrl(nodeAnnotation.docUrl()); + return nodeDefinition; + } + + private String[] getRelationTypesWithFailureRelation(RuleNode nodeAnnotation) { + List relationTypes = new ArrayList<>(Arrays.asList(nodeAnnotation.relationTypes())); + if (!relationTypes.contains(TbRelationTypes.FAILURE)) { + relationTypes.add(TbRelationTypes.FAILURE); + } + return relationTypes.toArray(new String[relationTypes.size()]); + } + private Set getBeanDefinitions(Class componentType) { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(componentType)); @@ -168,42 +200,30 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @Override public void discoverComponents() { - registerComponents(ComponentType.FILTER, Filter.class); - - registerComponents(ComponentType.PROCESSOR, Processor.class); - - registerComponents(ComponentType.ACTION, Action.class); - - registerComponents(ComponentType.PLUGIN, Plugin.class); - + registerRuleNodeComponents(); log.info("Found following definitions: {}", components.values()); } @Override public List getComponents(ComponentType type) { - return Collections.unmodifiableList(componentsMap.get(type)); + if (componentsMap.containsKey(type)) { + return Collections.unmodifiableList(componentsMap.get(type)); + } else { + return Collections.emptyList(); + } } @Override - public Optional getComponent(String clazz) { - return Optional.ofNullable(components.get(clazz)); + public List getComponents(Set types) { + List result = new ArrayList<>(); + types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> { + result.addAll(componentsMap.get(type)); + }); + return Collections.unmodifiableList(result); } @Override - public List getPluginActions(String pluginClazz) { - Optional pluginOpt = getComponent(pluginClazz); - if (pluginOpt.isPresent()) { - ComponentDescriptor plugin = pluginOpt.get(); - if (ComponentType.PLUGIN != plugin.getType()) { - throw new IllegalArgumentException(pluginClazz + " is not a plugin!"); - } - List result = new ArrayList<>(); - for (String action : plugin.getActions().split(",")) { - getComponent(action).ifPresent(v -> result.add(v)); - } - return result; - } else { - throw new IllegalArgumentException(pluginClazz + " is not a component!"); - } + public Optional getComponent(String clazz) { + return Optional.ofNullable(components.get(clazz)); } } diff --git a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java index ea27e60646..3ee3251242 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import java.util.List; import java.util.Optional; +import java.util.Set; /** * @author Andrew Shvayka @@ -30,8 +31,8 @@ public interface ComponentDiscoveryService { List getComponents(ComponentType type); - Optional getComponent(String clazz); + List getComponents(Set types); - List getPluginActions(String pluginClazz); + Optional getComponent(String clazz); } diff --git a/application/src/main/java/org/thingsboard/server/service/encoding/DataDecodingEncodingService.java b/application/src/main/java/org/thingsboard/server/service/encoding/DataDecodingEncodingService.java new file mode 100644 index 0000000000..248e9f362a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/encoding/DataDecodingEncodingService.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2018 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.encoding; + +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.gen.cluster.ClusterAPIProtos; + +import java.util.Optional; + +public interface DataDecodingEncodingService { + + Optional decode(byte[] byteArray); + + byte[] encode(TbActorMsg msq); + + ClusterAPIProtos.ClusterMessage convertToProtoDataMessage(ServerAddress serverAddress, + TbActorMsg msg); + +} + diff --git a/application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java b/application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java new file mode 100644 index 0000000000..2cf9299743 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2018 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.encoding; + +import com.google.protobuf.ByteString; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.SerializationUtils; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.gen.cluster.ClusterAPIProtos; + +import java.util.Optional; + +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE; + + +@Slf4j +@Service +public class ProtoWithJavaSerializationDecodingEncodingService implements DataDecodingEncodingService { + + + @Override + public Optional decode(byte[] byteArray) { + try { + TbActorMsg msg = (TbActorMsg) SerializationUtils.deserialize(byteArray); + return Optional.of(msg); + + } catch (IllegalArgumentException e) { + log.error("Error during deserialization message, [{}]", e.getMessage()); + return Optional.empty(); + } + } + + @Override + public byte[] encode(TbActorMsg msq) { + return SerializationUtils.serialize(msq); + } + + @Override + public ClusterAPIProtos.ClusterMessage convertToProtoDataMessage(ServerAddress serverAddress, + TbActorMsg msg) { + return ClusterAPIProtos.ClusterMessage + .newBuilder() + .setServerAddress(ClusterAPIProtos.ServerAddress + .newBuilder() + .setHost(serverAddress.getHost()) + .setPort(serverAddress.getPort()) + .build()) + .setMessageType(CLUSTER_ACTOR_MESSAGE) + .setPayload(ByteString.copyFrom(encode(msg))).build(); + + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/AbstractListeningExecutor.java b/application/src/main/java/org/thingsboard/server/service/executors/AbstractListeningExecutor.java new file mode 100644 index 0000000000..210d9da27c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/AbstractListeningExecutor.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2018 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.executors; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import org.thingsboard.rule.engine.api.ListeningExecutor; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; + +/** + * Created by igor on 4/13/18. + */ +public abstract class AbstractListeningExecutor implements ListeningExecutor { + + private ListeningExecutorService service; + + @PostConstruct + public void init() { + this.service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(getThreadPollSize())); + } + + @PreDestroy + public void destroy() { + if (this.service != null) { + this.service.shutdown(); + } + } + + @Override + public ListenableFuture executeAsync(Callable task) { + return service.submit(task); + } + + @Override + public void execute(Runnable command) { + service.execute(command); + } + + public ListeningExecutorService executor() { + return service; + } + + protected abstract int getThreadPollSize(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/DbCallbackExecutorService.java b/application/src/main/java/org/thingsboard/server/service/executors/DbCallbackExecutorService.java new file mode 100644 index 0000000000..2eec3ed234 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/DbCallbackExecutorService.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2018 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.executors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class DbCallbackExecutorService extends AbstractListeningExecutor { + + @Value("${actors.rule.db_callback_thread_pool_size}") + private int dbCallbackExecutorThreadPoolSize; + + @Override + protected int getThreadPollSize() { + return dbCallbackExecutorThreadPoolSize; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/ExternalCallExecutorService.java b/application/src/main/java/org/thingsboard/server/service/executors/ExternalCallExecutorService.java new file mode 100644 index 0000000000..3f6d87e41e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/ExternalCallExecutorService.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2018 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.executors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class ExternalCallExecutorService extends AbstractListeningExecutor { + + @Value("${actors.rule.external_call_thread_pool_size}") + private int externalCallExecutorThreadPoolSize; + + @Override + protected int getThreadPollSize() { + return externalCallExecutorThreadPoolSize; + } + +} + diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java index 5bdc0a7451..dd76b21844 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseSchemaService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; @@ -37,17 +36,16 @@ public class CassandraDatabaseSchemaService implements DatabaseSchemaService { private static final String CASSANDRA_DIR = "cassandra"; private static final String SCHEMA_CQL = "schema.cql"; - @Value("${install.data_dir}") - private String dataDir; - @Autowired private CassandraInstallCluster cluster; + @Autowired + private InstallScripts installScripts; + @Override public void createDatabaseSchema() throws Exception { log.info("Installing Cassandra DataBase schema..."); - - Path schemaFile = Paths.get(this.dataDir, CASSANDRA_DIR, SCHEMA_CQL); + Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, SCHEMA_CQL); loadCql(schemaFile); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index e6826ec736..4d2adeaad1 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.install; import com.datastax.driver.core.KeyspaceMetadata; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -33,7 +32,17 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import static org.thingsboard.server.service.install.DatabaseHelper.*; +import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; +import static org.thingsboard.server.service.install.DatabaseHelper.ASSET; +import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; +import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; +import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; +import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE; +import static org.thingsboard.server.service.install.DatabaseHelper.ID; +import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; +import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; @Service @NoSqlDao @@ -43,9 +52,6 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; - @Value("${install.data_dir}") - private String dataDir; - @Autowired private CassandraCluster cluster; @@ -55,6 +61,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @Autowired private DashboardService dashboardService; + @Autowired + private InstallScripts installScripts; + @Override public void upgradeDatabase(String fromVersion) throws Exception { @@ -91,7 +100,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Relations dumped."); log.info("Updating schema ..."); - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); loadCql(schemaUpdateFile); log.info("Schema updated."); @@ -173,7 +182,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); loadCql(schemaUpdateFile); log.info("Schema updated."); @@ -185,6 +194,14 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { Files.deleteIfExists(dashboardsDump); } log.info("Dashboards restored."); + break; + case "1.4.0": + + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_CQL); + loadCql(schemaUpdateFile); + log.info("Schema updated."); + break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java b/application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java rename to application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java index 3f3bd36f65..94273a25de 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorChain.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.service.install; -public interface RuleActorChain { +public interface DataUpdateService { - int size(); - - RuleActorMetaData getRuleActorMd(int index); + void updateData(String fromVersion) throws Exception; } diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java index 4a21412e3b..2f9b4a5ed3 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java @@ -30,7 +30,11 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; /** * Created by igor on 2/27/18. diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java new file mode 100644 index 0000000000..b3723682e2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java @@ -0,0 +1,106 @@ +/** + * Copyright © 2016-2018 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.install; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.IdBased; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.List; +import java.util.UUID; + +@Service +@Profile("install") +@Slf4j +public class DefaultDataUpdateService implements DataUpdateService { + + @Autowired + private TenantService tenantService; + + @Autowired + private RuleChainService ruleChainService; + + @Autowired + private InstallScripts installScripts; + + @Override + public void updateData(String fromVersion) throws Exception { + switch (fromVersion) { + case "1.4.0": + log.info("Updating data from version 1.4.0 to 2.0.0 ..."); + tenantsDefaultRuleChainUpdater.updateEntities(null); + break; + default: + throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); + } + } + + private PaginatedUpdater tenantsDefaultRuleChainUpdater = + new PaginatedUpdater() { + + @Override + protected List findEntities(String region, TextPageLink pageLink) { + return tenantService.findTenants(pageLink).getData(); + } + + @Override + protected void updateEntity(Tenant tenant) { + try { + RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId()); + if (ruleChain == null) { + installScripts.createDefaultRuleChains(tenant.getId()); + } + } catch (Exception e) { + log.error("Unable to update Tenant", e); + } + } + }; + + public abstract class PaginatedUpdater> { + + private static final int DEFAULT_LIMIT = 100; + + public void updateEntities(I id) { + TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT); + boolean hasNext = true; + while (hasNext) { + List entities = findEntities(id, pageLink); + for (D entity : entities) { + updateEntity(entity); + } + hasNext = entities.size() == pageLink.getLimit(); + if (hasNext) { + int index = entities.size() - 1; + UUID idOffset = entities.get(index).getUuidId(); + pageLink.setIdOffset(idOffset); + } + } + } + + protected abstract List findEntities(I id, TextPageLink pageLink); + + protected abstract void updateEntity(D entity); + + } + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 1ef805f3c9..aa8c60b467 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -15,66 +15,45 @@ */ package org.thingsboard.server.service.install; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Profile; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.rule.RuleService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; -import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - @Service @Profile("install") @Slf4j public class DefaultSystemDataLoaderService implements SystemDataLoaderService { - private static final String JSON_DIR = "json"; - private static final String SYSTEM_DIR = "system"; - private static final String DEMO_DIR = "demo"; - private static final String WIDGET_BUNDLES_DIR = "widget_bundles"; - private static final String PLUGINS_DIR = "plugins"; - private static final String RULES_DIR = "rules"; - private static final String DASHBOARDS_DIR = "dashboards"; - private static final ObjectMapper objectMapper = new ObjectMapper(); - public static final String JSON_EXT = ".json"; public static final String CUSTOMER_CRED = "customer"; public static final String DEFAULT_DEVICE_TYPE = "default"; - @Value("${install.data_dir}") - private String dataDir; + @Autowired + private InstallScripts installScripts; @Autowired private BCryptPasswordEncoder passwordEncoder; @@ -88,15 +67,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Autowired private WidgetsBundleService widgetsBundleService; - @Autowired - private WidgetTypeService widgetTypeService; - - @Autowired - private PluginService pluginService; - - @Autowired - private RuleService ruleService; - @Autowired private TenantService tenantService; @@ -109,9 +79,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Autowired private DeviceCredentialsService deviceCredentialsService; - @Autowired - private DashboardService dashboardService; - @Bean protected BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); @@ -146,56 +113,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { adminSettingsService.saveAdminSettings(mailSettings); } - @Override - public void loadSystemWidgets() throws Exception { - Path widgetBundlesDir = Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); - try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { - dirStream.forEach( - path -> { - try { - JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile()); - JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); - WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); - widgetTypesArrayJson.forEach( - widgetTypeJson -> { - try { - WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetTypeService.saveWidgetType(widgetType); - } catch (Exception e) { - log.error("Unable to load widget type from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widget type from json", e); - } - } - ); - } catch (Exception e) { - log.error("Unable to load widgets bundle from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widgets bundle from json", e); - } - } - ); - } - } - - @Override - public void loadSystemPlugins() throws Exception { - loadPlugins(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, PLUGINS_DIR), null); - } - - - @Override - public void loadSystemRules() throws Exception { - loadRules(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, RULES_DIR), null); - } - @Override public void loadDemoData() throws Exception { Tenant demoTenant = new Tenant(); demoTenant.setRegion("Global"); demoTenant.setTitle("Tenant"); demoTenant = tenantService.saveTenant(demoTenant); + installScripts.createDefaultRuleChains(demoTenant.getId()); createUser(Authority.TENANT_ADMIN, demoTenant.getId(), null, "tenant@thingsboard.org", "tenant"); Customer customerA = new Customer(); @@ -227,9 +151,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + "Raspberry Pi GPIO control sample application"); - loadPlugins(Paths.get(dataDir, JSON_DIR, DEMO_DIR, PLUGINS_DIR), demoTenant.getId()); - loadRules(Paths.get(dataDir, JSON_DIR, DEMO_DIR, RULES_DIR), demoTenant.getId()); - loadDashboards(Paths.get(dataDir, JSON_DIR, DEMO_DIR, DASHBOARDS_DIR), demoTenant.getId(), null); + installScripts.loadDashboards(demoTenant.getId(), null); } @Override @@ -240,6 +162,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } } + @Override + public void loadSystemWidgets() throws Exception { + installScripts.loadSystemWidgets(); + } + private User createUser(Authority authority, TenantId tenantId, CustomerId customerId, @@ -282,72 +209,4 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { return device; } - private void loadPlugins(Path pluginsDir, TenantId tenantId) throws Exception{ - try (DirectoryStream dirStream = Files.newDirectoryStream(pluginsDir, path -> path.toString().endsWith(JSON_EXT))) { - dirStream.forEach( - path -> { - try { - JsonNode pluginJson = objectMapper.readTree(path.toFile()); - PluginMetaData plugin = objectMapper.treeToValue(pluginJson, PluginMetaData.class); - plugin.setTenantId(tenantId); - if (plugin.getState() == ComponentLifecycleState.ACTIVE) { - plugin.setState(ComponentLifecycleState.SUSPENDED); - PluginMetaData savedPlugin = pluginService.savePlugin(plugin); - pluginService.activatePluginById(savedPlugin.getId()); - } else { - pluginService.savePlugin(plugin); - } - } catch (Exception e) { - log.error("Unable to load plugin from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load plugin from json", e); - } - } - ); - } - } - - private void loadRules(Path rulesDir, TenantId tenantId) throws Exception { - try (DirectoryStream dirStream = Files.newDirectoryStream(rulesDir, path -> path.toString().endsWith(JSON_EXT))) { - dirStream.forEach( - path -> { - try { - JsonNode ruleJson = objectMapper.readTree(path.toFile()); - RuleMetaData rule = objectMapper.treeToValue(ruleJson, RuleMetaData.class); - rule.setTenantId(tenantId); - if (rule.getState() == ComponentLifecycleState.ACTIVE) { - rule.setState(ComponentLifecycleState.SUSPENDED); - RuleMetaData savedRule = ruleService.saveRule(rule); - ruleService.activateRuleById(savedRule.getId()); - } else { - ruleService.saveRule(rule); - } - } catch (Exception e) { - log.error("Unable to load rule from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load rule from json", e); - } - } - ); - } - } - - private void loadDashboards(Path dashboardsDir, TenantId tenantId, CustomerId customerId) throws Exception { - try (DirectoryStream dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { - dirStream.forEach( - path -> { - try { - JsonNode dashboardJson = objectMapper.readTree(path.toFile()); - Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); - dashboard.setTenantId(tenantId); - Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); - if (customerId != null && !customerId.isNullUid()) { - dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); - } - } catch (Exception e) { - log.error("Unable to load dashboard from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load dashboard from json", e); - } - } - ); - } - } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java new file mode 100644 index 0000000000..0ad0edef13 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -0,0 +1,184 @@ +/** + * Copyright © 2016-2018 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.install; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; + +/** + * Created by ashvayka on 18.04.18. + */ +@Component +@Slf4j +public class InstallScripts { + + public static final String APP_DIR = "application"; + public static final String SRC_DIR = "src"; + public static final String MAIN_DIR = "main"; + public static final String DATA_DIR = "data"; + public static final String JSON_DIR = "json"; + public static final String SYSTEM_DIR = "system"; + public static final String TENANT_DIR = "tenant"; + public static final String DEMO_DIR = "demo"; + public static final String RULE_CHAINS_DIR = "rule_chains"; + public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; + public static final String DASHBOARDS_DIR = "dashboards"; + + public static final String JSON_EXT = ".json"; + + @Value("${install.data_dir:}") + private String dataDir; + + @Autowired + private RuleChainService ruleChainService; + + @Autowired + private DashboardService dashboardService; + + @Autowired + private WidgetTypeService widgetTypeService; + + @Autowired + private WidgetsBundleService widgetsBundleService; + + public Path getTenantRuleChainsDir() { + return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); + } + + public String getDataDir() { + if (!StringUtils.isEmpty(dataDir)) { + if (!Paths.get(this.dataDir).toFile().isDirectory()) { + throw new RuntimeException("'install.data_dir' property value is not a valid directory!"); + } + return dataDir; + } else { + String workDir = System.getProperty("user.dir"); + if (workDir.endsWith("application")) { + return Paths.get(workDir, SRC_DIR, MAIN_DIR, DATA_DIR).toString(); + } else { + Path dataDirPath = Paths.get(workDir, APP_DIR, SRC_DIR, MAIN_DIR, DATA_DIR); + if (Files.exists(dataDirPath)) { + return dataDirPath.toString(); + } else { + throw new RuntimeException("Not valid working directory: " + workDir + ". Please use either root project directory, application module directory or specify valid \"install.data_dir\" ENV variable to avoid automatic data directory lookup!"); + } + } + } + } + + public void createDefaultRuleChains(TenantId tenantId) throws IOException { + Path tenantChainsDir = getTenantRuleChainsDir(); + try (DirectoryStream dirStream = Files.newDirectoryStream(tenantChainsDir, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) { + dirStream.forEach( + path -> { + try { + JsonNode ruleChainJson = objectMapper.readTree(path.toFile()); + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); + + ruleChain.setTenantId(tenantId); + ruleChain = ruleChainService.saveRuleChain(ruleChain); + + ruleChainMetaData.setRuleChainId(ruleChain.getId()); + ruleChainService.saveRuleChainMetaData(ruleChainMetaData); + } catch (Exception e) { + log.error("Unable to load rule chain from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load rule chain from json", e); + } + } + ); + } + } + + public void loadSystemWidgets() throws Exception { + Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); + try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { + dirStream.forEach( + path -> { + try { + JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile()); + JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); + WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class); + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); + widgetTypesArrayJson.forEach( + widgetTypeJson -> { + try { + WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class); + widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); + widgetTypeService.saveWidgetType(widgetType); + } catch (Exception e) { + log.error("Unable to load widget type from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widget type from json", e); + } + } + ); + } catch (Exception e) { + log.error("Unable to load widgets bundle from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widgets bundle from json", e); + } + } + ); + } + } + + public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { + Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); + try (DirectoryStream dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { + dirStream.forEach( + path -> { + try { + JsonNode dashboardJson = objectMapper.readTree(path.toFile()); + Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); + dashboard.setTenantId(tenantId); + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); + if (customerId != null && !customerId.isNullUid()) { + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); + } + } catch (Exception e) { + log.error("Unable to load dashboard from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load dashboard from json", e); + } + } + ); + } + } + + +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java index 443ec0c5e0..1daf66086a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseSchemaService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -27,7 +28,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; -import java.sql.PreparedStatement; @Service @Profile("install") @@ -38,9 +38,6 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { private static final String SQL_DIR = "sql"; private static final String SCHEMA_SQL = "schema.sql"; - @Value("${install.data_dir}") - private String dataDir; - @Value("${spring.datasource.url}") private String dbUrl; @@ -50,12 +47,15 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { @Value("${spring.datasource.password}") private String dbPassword; + @Autowired + private InstallScripts installScripts; + @Override public void createDatabaseSchema() throws Exception { log.info("Installing SQL DataBase schema..."); - Path schemaFile = Paths.get(this.dataDir, SQL_DIR, SCHEMA_SQL); + Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, SCHEMA_SQL); try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8")); conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index cdd3103358..29d5c65be9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -22,7 +22,6 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.util.SqlDao; -import org.thingsboard.server.service.install.cql.CassandraDbHelper; import org.thingsboard.server.service.install.sql.SqlDbHelper; import java.nio.charset.Charset; @@ -30,11 +29,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; -import java.sql.DatabaseMetaData; import java.sql.DriverManager; -import static org.thingsboard.server.service.install.DatabaseHelper.*; +import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; +import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; +import static org.thingsboard.server.service.install.DatabaseHelper.ID; +import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; +import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; @Service @Profile("install") @@ -44,9 +48,6 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { private static final String SCHEMA_UPDATE_SQL = "schema_update.sql"; - @Value("${install.data_dir}") - private String dataDir; - @Value("${spring.datasource.url}") private String dbUrl; @@ -59,12 +60,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @Autowired private DashboardService dashboardService; + @Autowired + private InstallScripts installScripts; + @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { case "1.3.0": log.info("Updating schema ..."); - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script @@ -82,7 +86,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Dashboards dumped."); log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script log.info("Schema updated."); @@ -97,6 +101,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Dashboards restored."); } break; + case "1.4.0": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_SQL); + String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); + conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + log.info("Schema updated."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index a3dcb68573..f3a6af4880 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -23,10 +23,6 @@ public interface SystemDataLoaderService { void loadSystemWidgets() throws Exception; - void loadSystemPlugins() throws Exception; - - void loadSystemRules() throws Exception; - void loadDemoData() throws Exception; void deleteSystemWidgetBundle(String bundleAlias) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java index c13d70ecb7..bdf980fea2 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java @@ -15,7 +15,16 @@ */ package org.thingsboard.server.service.install.cql; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.TableMetadata; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVPrinter; @@ -25,7 +34,11 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; diff --git a/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java index f6c4749ea5..6e3d354ab4 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java @@ -23,7 +23,12 @@ import org.apache.commons.csv.CSVRecord; import java.nio.file.Files; import java.nio.file.Path; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 25b911c2d5..818c935595 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -27,13 +27,15 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import org.springframework.ui.velocity.VelocityEngineUtils; +import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; -import org.thingsboard.server.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardException; import javax.annotation.PostConstruct; +import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.util.HashMap; import java.util.Locale; @@ -49,18 +51,18 @@ public class DefaultMailService implements MailService { public static final String UTF_8 = "UTF-8"; @Autowired private MessageSource messages; - + @Autowired @Qualifier("velocityEngine") private VelocityEngine engine; - + private JavaMailSenderImpl mailSender; - + private String mailFrom; - + @Autowired - private AdminSettingsService adminSettingsService; - + private AdminSettingsService adminSettingsService; + @PostConstruct private void init() { updateMailConfiguration(); @@ -77,7 +79,7 @@ public class DefaultMailService implements MailService { throw new IncorrectParameterException("Failed to date mail configuration. Settings not found!"); } } - + private JavaMailSenderImpl createMailSender(JsonNode jsonConfig) { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost(jsonConfig.get("smtpHost").asText()); @@ -99,7 +101,7 @@ public class DefaultMailService implements MailService { javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false"); return javaMailProperties; } - + private int parsePort(String strPort) { try { return Integer.valueOf(strPort); @@ -112,86 +114,102 @@ public class DefaultMailService implements MailService { public void sendEmail(String email, String subject, String message) throws ThingsboardException { sendMail(mailSender, mailFrom, email, subject, message); } - + @Override public void sendTestMail(JsonNode jsonConfig, String email) throws ThingsboardException { JavaMailSenderImpl testMailSender = createMailSender(jsonConfig); String mailFrom = jsonConfig.get("mailFrom").asText(); String subject = messages.getMessage("test.message.subject", null, Locale.US); - + Map model = new HashMap(); model.put(TARGET_EMAIL, email); - + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, "test.vm", UTF_8, model); - - sendMail(testMailSender, mailFrom, email, subject, message); + + sendMail(testMailSender, mailFrom, email, subject, message); } @Override public void sendActivationEmail(String activationLink, String email) throws ThingsboardException { - + String subject = messages.getMessage("activation.subject", null, Locale.US); - + Map model = new HashMap(); model.put("activationLink", activationLink); model.put(TARGET_EMAIL, email); - + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, "activation.vm", UTF_8, model); - - sendMail(mailSender, mailFrom, email, subject, message); + + sendMail(mailSender, mailFrom, email, subject, message); } - + @Override public void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException { - + String subject = messages.getMessage("account.activated.subject", null, Locale.US); - + Map model = new HashMap(); model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, "account.activated.vm", UTF_8, model); - - sendMail(mailSender, mailFrom, email, subject, message); + + sendMail(mailSender, mailFrom, email, subject, message); } @Override public void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException { - + String subject = messages.getMessage("reset.password.subject", null, Locale.US); - + Map model = new HashMap(); model.put("passwordResetLink", passwordResetLink); model.put(TARGET_EMAIL, email); - + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, "reset.password.vm", UTF_8, model); - - sendMail(mailSender, mailFrom, email, subject, message); + + sendMail(mailSender, mailFrom, email, subject, message); } - + @Override public void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException { - + String subject = messages.getMessage("password.was.reset.subject", null, Locale.US); - + Map model = new HashMap(); model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - + String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, "password.was.reset.vm", UTF_8, model); - - sendMail(mailSender, mailFrom, email, subject, message); + + sendMail(mailSender, mailFrom, email, subject, message); } + @Override + public void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException { + MimeMessage mailMsg = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8"); + helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from); + helper.setTo(to.split("\\s*,\\s*")); + if (!StringUtils.isBlank(cc)) { + helper.setCc(cc.split("\\s*,\\s*")); + } + if (!StringUtils.isBlank(bcc)) { + helper.setBcc(bcc.split("\\s*,\\s*")); + } + helper.setSubject(subject); + helper.setText(body); + mailSender.send(helper.getMimeMessage()); + } - private void sendMail(JavaMailSenderImpl mailSender, - String mailFrom, String email, - String subject, String message) throws ThingsboardException { + private void sendMail(JavaMailSenderImpl mailSender, + String mailFrom, String email, + String subject, String message) throws ThingsboardException { try { MimeMessage mimeMsg = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8); @@ -208,7 +226,7 @@ public class DefaultMailService implements MailService { protected ThingsboardException handleException(Exception exception) { String message; if (exception instanceof NestedRuntimeException) { - message = ((NestedRuntimeException)exception).getMostSpecificCause().getMessage(); + message = ((NestedRuntimeException) exception).getMostSpecificCause().getMessage(); } else { message = exception.getMessage(); } diff --git a/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java b/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java new file mode 100644 index 0000000000..e8caab62a6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2018 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.mail; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.service.executors.AbstractListeningExecutor; + +@Component +public class MailExecutorService extends AbstractListeningExecutor { + + @Value("${actors.rule.mail_thread_pool_size}") + private int mailExecutorThreadPoolSize; + + @Override + protected int getThreadPollSize() { + return mailExecutorThreadPoolSize; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java new file mode 100644 index 0000000000..927584789d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultMsgQueueService.java @@ -0,0 +1,111 @@ +/** + * Copyright © 2016-2018 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.queue; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.transport.quota.tenant.TenantQuotaService; +import org.thingsboard.server.dao.queue.MsgQueue; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +@Service +@Slf4j +public class DefaultMsgQueueService implements MsgQueueService { + + @Value("${actors.rule.queue.max_size}") + private long queueMaxSize; + + @Value("${actors.rule.queue.cleanup_period}") + private long queueCleanUpPeriod; + + @Autowired + private MsgQueue msgQueue; + + @Autowired + private TenantQuotaService quotaService; + + private ScheduledExecutorService cleanupExecutor; + + private Map pendingCountPerTenant = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + if (queueCleanUpPeriod > 0) { + cleanupExecutor = Executors.newSingleThreadScheduledExecutor(); + cleanupExecutor.scheduleAtFixedRate(() -> cleanup(), + queueCleanUpPeriod, queueCleanUpPeriod, TimeUnit.SECONDS); + } + } + + @PreDestroy + public void stop() { + if (cleanupExecutor != null) { + cleanupExecutor.shutdownNow(); + } + } + + @Override + public ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { + if(quotaService.isQuotaExceeded(tenantId.getId().toString())) { + log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId()); + return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded")); + } + + AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong()); + if (pendingMsgCount.incrementAndGet() < queueMaxSize) { + return msgQueue.put(tenantId, msg, nodeId, clusterPartition); + } else { + pendingMsgCount.decrementAndGet(); + return Futures.immediateFailedFuture(new RuntimeException("Message queue is full!")); + } + } + + @Override + public ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { + ListenableFuture result = msgQueue.ack(tenantId, msg, nodeId, clusterPartition); + AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong()); + pendingMsgCount.decrementAndGet(); + return result; + } + + @Override + public Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) { + return msgQueue.findUnprocessed(tenantId, nodeId, clusterPartition); + } + + private void cleanup() { + pendingCountPerTenant.forEach((tenantId, pendingMsgCount) -> { + pendingMsgCount.set(0); + msgQueue.cleanUp(tenantId); + }); + } + +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ResponsePluginToRuleMsg.java b/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java similarity index 59% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ResponsePluginToRuleMsg.java rename to application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java index 22491c865b..2cf001ba73 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ResponsePluginToRuleMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/MsgQueueService.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.service.queue; -import org.thingsboard.server.common.data.id.RuleId; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; +import org.thingsboard.server.common.msg.TbMsg; import java.util.UUID; -public class ResponsePluginToRuleMsg extends AbstractPluginToRuleMsg{ +public interface MsgQueueService { - private static final long serialVersionUID = 1L; + ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - public ResponsePluginToRuleMsg(UUID uid, TenantId tenantId, RuleId ruleId, ToDeviceMsg payload) { - super(uid, tenantId, ruleId, payload); - } + ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); + + Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition); } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java new file mode 100644 index 0000000000..678495bf46 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java @@ -0,0 +1,152 @@ +/** + * Copyright © 2016-2018 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.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.rule.engine.api.RpcError; +import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; +import org.thingsboard.server.actors.service.ActorService; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.dao.audit.AuditLogService; +import org.thingsboard.server.gen.cluster.ClusterAPIProtos; +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; +import org.thingsboard.server.service.encoding.DataDecodingEncodingService; +import org.thingsboard.server.service.telemetry.sub.Subscription; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Created by ashvayka on 27.03.18. + */ +@Service +@Slf4j +public class DefaultDeviceRpcService implements DeviceRpcService { + + @Autowired + private ClusterRoutingService routingService; + + @Autowired + private ClusterRpcService rpcService; + + @Autowired + private ActorService actorService; + + private ScheduledExecutorService rpcCallBackExecutor; + + private final ConcurrentMap> localRpcRequests = new ConcurrentHashMap<>(); + + @PostConstruct + public void initExecutor() { + rpcCallBackExecutor = Executors.newSingleThreadScheduledExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (rpcCallBackExecutor != null) { + rpcCallBackExecutor.shutdownNow(); + } + } + + @Override + public void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer responseConsumer) { + log.trace("[{}] Processing local rpc call for device [{}]", request.getTenantId(), request.getDeviceId()); + sendRpcRequest(request); + UUID requestId = request.getId(); + localRpcRequests.put(requestId, responseConsumer); + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); + log.error("[{}] processing the request: [{}]", this.hashCode(), requestId); + rpcCallBackExecutor.schedule(() -> { + log.error("[{}] timeout the request: [{}]", this.hashCode(), requestId); + Consumer consumer = localRpcRequests.remove(requestId); + if (consumer != null) { + consumer.accept(new FromDeviceRpcResponse(requestId, null, null, RpcError.TIMEOUT)); + } + }, timeout, TimeUnit.MILLISECONDS); + } + + @Override + public void processRpcResponseFromDevice(FromDeviceRpcResponse response) { + log.error("[{}] response to request: [{}]", this.hashCode(), response.getId()); + if (routingService.getCurrentServer().equals(response.getServerAddress())) { + UUID requestId = response.getId(); + Consumer consumer = localRpcRequests.remove(requestId); + if (consumer != null) { + consumer.accept(response); + } else { + log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response); + } + } else { + ClusterAPIProtos.FromDeviceRPCResponseProto.Builder builder = ClusterAPIProtos.FromDeviceRPCResponseProto.newBuilder(); + builder.setRequestIdMSB(response.getId().getMostSignificantBits()); + builder.setRequestIdLSB(response.getId().getLeastSignificantBits()); + response.getResponse().ifPresent(builder::setResponse); + if (response.getError().isPresent()) { + builder.setError(response.getError().get().ordinal()); + } else { + builder.setError(-1); + } + rpcService.tell(response.getServerAddress(), ClusterAPIProtos.MessageType.CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE, builder.build().toByteArray()); + } + } + + @Override + public void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.FromDeviceRPCResponseProto proto; + try { + proto = ClusterAPIProtos.FromDeviceRPCResponseProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null; + FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), routingService.getCurrentServer(), + proto.getResponse(), error); + processRpcResponseFromDevice(response); + } + + @Override + public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) { + ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body)); + forward(deviceId, rpcMsg); + } + + private void sendRpcRequest(ToDeviceRpcRequest msg) { + ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(routingService.getCurrentServer(), msg); + log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg); + forward(msg.getDeviceId(), rpcMsg); + } + + private void forward(DeviceId deviceId, T msg) { + actorService.onMsg(new SendToClusterMsg(deviceId, msg)); + } +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetAttributesRequestRuleToPluginMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java similarity index 53% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetAttributesRequestRuleToPluginMsg.java rename to application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java index 566cc79308..8b4d47b033 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetAttributesRequestRuleToPluginMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DeviceRpcService.java @@ -13,21 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.service.rpc; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.core.GetAttributesRequest; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; + +import java.util.function.Consumer; /** - * @author Andrew Shvayka + * Created by ashvayka on 16.04.18. */ -public class GetAttributesRequestRuleToPluginMsg extends AbstractRuleToPluginMsg { +public interface DeviceRpcService { + + void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer responseConsumer); + + void processRpcResponseFromDevice(FromDeviceRpcResponse response); - private static final long serialVersionUID = 1L; + void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body); - public GetAttributesRequestRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, GetAttributesRequest payload) { - super(tenantId, customerId, deviceId, payload); - } + void processRemoteResponseFromDevice(ServerAddress serverAddress, byte[] bytes); } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/FromDeviceRpcResponse.java b/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java similarity index 84% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/FromDeviceRpcResponse.java rename to application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java index 6ca00d29b2..9c3ce9a834 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/FromDeviceRpcResponse.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponse.java @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.service.rpc; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; +import org.thingsboard.rule.engine.api.RpcError; +import org.thingsboard.server.common.msg.cluster.ServerAddress; import java.util.Optional; import java.util.UUID; @@ -30,6 +32,8 @@ import java.util.UUID; public class FromDeviceRpcResponse { @Getter private final UUID id; + @Getter + private final ServerAddress serverAddress; private final String response; private final RpcError error; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/LocalRequestMetaData.java b/application/src/main/java/org/thingsboard/server/service/rpc/LocalRequestMetaData.java similarity index 78% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/LocalRequestMetaData.java rename to application/src/main/java/org/thingsboard/server/service/rpc/LocalRequestMetaData.java index 67dff86b3d..1416c97963 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/LocalRequestMetaData.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/LocalRequestMetaData.java @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.rpc; +package org.thingsboard.server.service.rpc; import lombok.Data; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.service.security.model.SecurityUser; /** - * @author Andrew Shvayka + * Created by ashvayka on 16.04.18. */ @Data public class LocalRequestMetaData { private final ToDeviceRpcRequest request; + private final SecurityUser user; private final DeferredResult responseWriter; } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestPluginMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java similarity index 65% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestPluginMsg.java rename to application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java index faf169655d..37c4c00cd1 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestPluginMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java @@ -13,42 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.service.rpc; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; +import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import java.util.Optional; /** - * @author Andrew Shvayka + * Created by ashvayka on 16.04.18. */ @ToString @RequiredArgsConstructor -public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg { +public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { - private final ServerAddress serverAddress; - @Getter - private final PluginId pluginId; @Getter - private final TenantId pluginTenantId; + private final ServerAddress serverAddress; @Getter private final ToDeviceRpcRequest msg; - public ToDeviceRpcRequestPluginMsg(PluginId pluginId, TenantId pluginTenantId, ToDeviceRpcRequest msg) { - this(null, pluginId, pluginTenantId, msg); - } - - public Optional getServerAddress() { - return Optional.ofNullable(serverAddress); - } - @Override public DeviceId getDeviceId() { return msg.getDeviceId(); @@ -58,5 +48,9 @@ public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg public TenantId getTenantId() { return msg.getTenantId(); } -} + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/PluginRpcMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java similarity index 54% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/PluginRpcMsg.java rename to application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java index ed5e5b24d3..201f65613a 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rpc/PluginRpcMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/ToServerRpcResponseActorMsg.java @@ -13,34 +13,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.rpc; +package org.thingsboard.server.service.rpc; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; -import org.thingsboard.server.common.data.id.PluginId; +import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; +import java.util.Optional; + +/** + * Created by ashvayka on 16.04.18. + */ @ToString @RequiredArgsConstructor -public class PluginRpcMsg implements ToPluginActorMsg { +public class ToServerRpcResponseActorMsg implements ToDeviceActorNotificationMsg { + @Getter private final TenantId tenantId; - private final PluginId pluginId; + @Getter - private final RpcMsg rpcMsg; + private final DeviceId deviceId; - @Override - public TenantId getPluginTenantId() { - return tenantId; - } + @Getter + private final ToServerRpcResponseMsg msg; @Override - public PluginId getPluginId() { - return pluginId; + public MsgType getMsgType() { + return MsgType.SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG; } - - - } diff --git a/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java new file mode 100644 index 0000000000..7f274ec6d7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java @@ -0,0 +1,159 @@ +/** + * Copyright © 2016-2018 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.script; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import delight.nashornsandbox.NashornSandbox; +import delight.nashornsandbox.NashornSandboxes; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +public abstract class AbstractNashornJsSandboxService implements JsSandboxService { + + private NashornSandbox sandbox; + private ScriptEngine engine; + private ExecutorService monitorExecutorService; + + private Map functionsMap = new ConcurrentHashMap<>(); + + private Map blackListedFunctions = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + if (useJsSandbox()) { + sandbox = NashornSandboxes.create(); + monitorExecutorService = Executors.newFixedThreadPool(getMonitorThreadPoolSize()); + sandbox.setExecutor(monitorExecutorService); + sandbox.setMaxCPUTime(getMaxCpuTime()); + sandbox.allowNoBraces(false); + sandbox.setMaxPreparedStatements(30); + } else { + NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); + engine = factory.getScriptEngine(new String[]{"--no-java"}); + } + } + + @PreDestroy + public void stop() { + if (monitorExecutorService != null) { + monitorExecutorService.shutdownNow(); + } + } + + protected abstract boolean useJsSandbox(); + + protected abstract int getMonitorThreadPoolSize(); + + protected abstract long getMaxCpuTime(); + + protected abstract int getMaxErrors(); + + @Override + public ListenableFuture eval(JsScriptType scriptType, String scriptBody, String... argNames) { + UUID scriptId = UUID.randomUUID(); + String functionName = "invokeInternal_" + scriptId.toString().replace('-','_'); + String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); + try { + if (useJsSandbox()) { + sandbox.eval(jsScript); + } else { + engine.eval(jsScript); + } + functionsMap.put(scriptId, functionName); + } catch (Exception e) { + log.warn("Failed to compile JS script: {}", e.getMessage(), e); + return Futures.immediateFailedFuture(e); + } + return Futures.immediateFuture(scriptId); + } + + @Override + public ListenableFuture invokeFunction(UUID scriptId, Object... args) { + String functionName = functionsMap.get(scriptId); + if (functionName == null) { + return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!")); + } + if (!isBlackListed(scriptId)) { + try { + Object result; + if (useJsSandbox()) { + result = sandbox.getSandboxedInvocable().invokeFunction(functionName, args); + } else { + result = ((Invocable)engine).invokeFunction(functionName, args); + } + return Futures.immediateFuture(result); + } catch (Exception e) { + blackListedFunctions.computeIfAbsent(scriptId, key -> new AtomicInteger(0)).incrementAndGet(); + return Futures.immediateFailedFuture(e); + } + } else { + return Futures.immediateFailedFuture( + new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!")); + } + } + + @Override + public ListenableFuture release(UUID scriptId) { + String functionName = functionsMap.get(scriptId); + if (functionName != null) { + try { + if (useJsSandbox()) { + sandbox.eval(functionName + " = undefined;"); + } else { + engine.eval(functionName + " = undefined;"); + } + functionsMap.remove(scriptId); + blackListedFunctions.remove(scriptId); + } catch (ScriptException e) { + return Futures.immediateFailedFuture(e); + } + } + return Futures.immediateFuture(null); + } + + private boolean isBlackListed(UUID scriptId) { + if (blackListedFunctions.containsKey(scriptId)) { + AtomicInteger errorCount = blackListedFunctions.get(scriptId); + return errorCount.get() >= getMaxErrors(); + } else { + return false; + } + } + + private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) { + switch (scriptType) { + case RULE_NODE_SCRIPT: + return RuleNodeScriptFactory.generateRuleNodeScript(functionName, scriptBody, argNames); + default: + throw new RuntimeException("No script factory implemented for scriptType: " + scriptType); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/script/JsExecutorService.java b/application/src/main/java/org/thingsboard/server/service/script/JsExecutorService.java new file mode 100644 index 0000000000..c60f45550f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/script/JsExecutorService.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2018 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.script; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.service.executors.AbstractListeningExecutor; + +@Component +public class JsExecutorService extends AbstractListeningExecutor { + + @Value("${actors.rule.js_thread_pool_size}") + private int jsExecutorThreadPoolSize; + + @Override + protected int getThreadPollSize() { + return jsExecutorThreadPoolSize; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java b/application/src/main/java/org/thingsboard/server/service/script/JsSandboxService.java similarity index 63% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java rename to application/src/main/java/org/thingsboard/server/service/script/JsSandboxService.java index 0258bce130..ee86c62a25 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleToPluginTimeoutMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/script/JsSandboxService.java @@ -13,24 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; -import java.io.Serializable; -import java.util.UUID; +package org.thingsboard.server.service.script; + +import com.google.common.util.concurrent.ListenableFuture; -public class RuleToPluginTimeoutMsg implements Serializable { +import java.util.UUID; - private static final long serialVersionUID = 1L; +public interface JsSandboxService { - private final UUID msgId; + ListenableFuture eval(JsScriptType scriptType, String scriptBody, String... argNames); - public RuleToPluginTimeoutMsg(UUID msgId) { - super(); - this.msgId = msgId; - } + ListenableFuture invokeFunction(UUID scriptId, Object... args); - public UUID getMsgId() { - return msgId; - } + ListenableFuture release(UUID scriptId); } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java b/application/src/main/java/org/thingsboard/server/service/script/JsScriptType.java similarity index 86% rename from application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java rename to application/src/main/java/org/thingsboard/server/service/script/JsScriptType.java index b2eb53fe3f..f7a4748f0e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/CompoundRuleActorChain.java +++ b/application/src/main/java/org/thingsboard/server/service/script/JsScriptType.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; -public class CompoundRuleActorChain { +package org.thingsboard.server.service.script; +public enum JsScriptType { + RULE_NODE_SCRIPT } diff --git a/application/src/main/java/org/thingsboard/server/service/script/NashornJsSandboxService.java b/application/src/main/java/org/thingsboard/server/service/script/NashornJsSandboxService.java new file mode 100644 index 0000000000..3e8b4e9c55 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/script/NashornJsSandboxService.java @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2018 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.script; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class NashornJsSandboxService extends AbstractNashornJsSandboxService { + + @Value("${actors.rule.js_sandbox.use_js_sandbox}") + private boolean useJsSandbox; + + @Value("${actors.rule.js_sandbox.monitor_thread_pool_size}") + private int monitorThreadPoolSize; + + @Value("${actors.rule.js_sandbox.max_cpu_time}") + private long maxCpuTime; + + @Value("${actors.rule.js_sandbox.max_errors}") + private int maxErrors; + + @Override + protected boolean useJsSandbox() { + return useJsSandbox; + } + + @Override + protected int getMonitorThreadPoolSize() { + return monitorThreadPoolSize; + } + + @Override + protected long getMaxCpuTime() { + return maxCpuTime; + } + + @Override + protected int getMaxErrors() { + return maxErrors; + } +} 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 new file mode 100644 index 0000000000..98b0e77a0a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java @@ -0,0 +1,181 @@ +/** + * Copyright © 2016-2018 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.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 lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import javax.script.ScriptException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + + +@Slf4j +public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.ScriptEngine { + + private static final ObjectMapper mapper = new ObjectMapper(); + private final JsSandboxService sandboxService; + + private final UUID scriptId; + + public RuleNodeJsScriptEngine(JsSandboxService sandboxService, String script, String... argNames) { + this.sandboxService = sandboxService; + try { + this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); + } catch (Exception e) { + throw new IllegalArgumentException("Can't compile script: " + e.getMessage()); + } + } + + private static String[] prepareArgs(TbMsg msg) { + try { + String[] args = new String[3]; + if (msg.getData() != null) { + args[0] = msg.getData(); + } else { + args[0] = ""; + } + args[1] = mapper.writeValueAsString(msg.getMetaData().getData()); + args[2] = msg.getType(); + return args; + } catch (Throwable th) { + throw new IllegalArgumentException("Cannot bind js args", th); + } + } + + private static TbMsg unbindMsg(JsonNode msgData, TbMsg msg) { + try { + String data = null; + Map metadata = null; + String messageType = null; + if (msgData.has(RuleNodeScriptFactory.MSG)) { + JsonNode msgPayload = msgData.get(RuleNodeScriptFactory.MSG); + data = mapper.writeValueAsString(msgPayload); + } + if (msgData.has(RuleNodeScriptFactory.METADATA)) { + JsonNode msgMetadata = msgData.get(RuleNodeScriptFactory.METADATA); + metadata = mapper.convertValue(msgMetadata, new TypeReference>() { + }); + } + if (msgData.has(RuleNodeScriptFactory.MSG_TYPE)) { + messageType = msgData.get(RuleNodeScriptFactory.MSG_TYPE).asText(); + } + String newData = data != null ? data : msg.getData(); + TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData().copy(); + String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); + return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData, msg.getRuleChainId(), msg.getRuleNodeId(), msg.getClusterPartition()); + } catch (Throwable th) { + th.printStackTrace(); + throw new RuntimeException("Failed to unbind message data from javascript result", th); + } + } + + @Override + public TbMsg executeUpdate(TbMsg msg) throws ScriptException { + JsonNode result = executeScript(msg); + if (!result.isObject()) { + log.warn("Wrong result type: {}", result.getNodeType()); + throw new ScriptException("Wrong result type: " + result.getNodeType()); + } + return unbindMsg(result, msg); + } + + @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); + } + + @Override + public JsonNode executeJson(TbMsg msg) throws ScriptException { + return executeScript(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(); + } + + @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()); + } + return result.asBoolean(); + } + + @Override + public Set executeSwitch(TbMsg msg) throws ScriptException { + JsonNode result = executeScript(msg); + if (result.isTextual()) { + return Collections.singleton(result.asText()); + } else if (result.isArray()) { + Set nextStates = Sets.newHashSet(); + for (JsonNode val : result) { + if (!val.isTextual()) { + log.warn("Wrong result type: {}", val.getNodeType()); + throw 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()); + } + } + + private JsonNode executeScript(TbMsg msg) throws ScriptException { + try { + String[] inArgs = prepareArgs(msg); + String eval = sandboxService.invokeFunction(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 { + throw new ScriptException("Failed to execute js script: " + e.getMessage()); + } + } catch (Exception e) { + throw new ScriptException("Failed to execute js script: " + e.getMessage()); + } + } + + public void destroy() { + sandboxService.release(this.scriptId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/script/RuleNodeScriptFactory.java b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeScriptFactory.java new file mode 100644 index 0000000000..5cc9c55b6f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/script/RuleNodeScriptFactory.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2018 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.script; + +public class RuleNodeScriptFactory { + + public static final String MSG = "msg"; + public static final String METADATA = "metadata"; + public static final String MSG_TYPE = "msgType"; + public static final String RULE_NODE_FUNCTION_NAME = "ruleNodeFunc"; + + private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr, msgType) { " + + " var msg = JSON.parse(msgStr); " + + " var metadata = JSON.parse(metadataStr); " + + " return JSON.stringify(%s(msg, metadata, msgType));" + + " function %s(%s, %s, %s) {"; + private static final String JS_WRAPPER_SUFFIX = "}" + + "\n}"; + + + public static String generateRuleNodeScript(String functionName, String scriptBody, String... argNames) { + String msgArg; + String metadataArg; + String msgTypeArg; + if (argNames != null && argNames.length == 3) { + msgArg = argNames[0]; + metadataArg = argNames[1]; + msgTypeArg = argNames[2]; + } else { + msgArg = MSG; + metadataArg = METADATA; + msgTypeArg = MSG_TYPE; + } + String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName, + RULE_NODE_FUNCTION_NAME, RULE_NODE_FUNCTION_NAME, msgArg, metadataArg, msgTypeArg); + return jsWrapperPrefix + scriptBody + JS_WRAPPER_SUFFIX; + } + +} 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 new file mode 100644 index 0000000000..600820ec73 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -0,0 +1,321 @@ +/** + * Copyright © 2016-2018 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.security; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.controller.HttpValidationCallback; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.BiConsumer; + +/** + * Created by ashvayka on 27.03.18. + */ +@Component +public class AccessValidator { + + public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; + public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; + public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; + + @Autowired + protected TenantService tenantService; + + @Autowired + protected CustomerService customerService; + + @Autowired + protected UserService userService; + + @Autowired + protected DeviceService deviceService; + + @Autowired + protected AssetService assetService; + + @Autowired + protected AlarmService alarmService; + + @Autowired + protected RuleChainService ruleChainService; + + private ExecutorService executor; + + @PostConstruct + public void initExecutor() { + executor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (executor != null) { + executor.shutdownNow(); + } + } + + public DeferredResult validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, + BiConsumer, EntityId> onSuccess) throws ThingsboardException { + return validateEntityAndCallback(currentUser, entityType, entityIdStr, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); + } + + public DeferredResult validateEntityAndCallback(SecurityUser currentUser, String entityType, String entityIdStr, + BiConsumer, EntityId> onSuccess, + BiConsumer, Throwable> onFailure) throws ThingsboardException { + return validateEntityAndCallback(currentUser, EntityIdFactory.getByTypeAndId(entityType, entityIdStr), + onSuccess, onFailure); + } + + public DeferredResult validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, + BiConsumer, EntityId> onSuccess) throws ThingsboardException { + return validateEntityAndCallback(currentUser, entityId, onSuccess, (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR)); + } + + public DeferredResult validateEntityAndCallback(SecurityUser currentUser, EntityId entityId, + BiConsumer, EntityId> onSuccess, + BiConsumer, Throwable> onFailure) throws ThingsboardException { + + final DeferredResult response = new DeferredResult<>(); + + validate(currentUser, entityId, new HttpValidationCallback(response, + new FutureCallback>() { + @Override + public void onSuccess(@Nullable DeferredResult result) { + onSuccess.accept(response, entityId); + } + + @Override + public void onFailure(Throwable t) { + onFailure.accept(response, t); + } + })); + + return response; + } + + public void validate(SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + switch (entityId.getEntityType()) { + case DEVICE: + validateDevice(currentUser, entityId, callback); + return; + case ASSET: + validateAsset(currentUser, entityId, callback); + return; + case RULE_CHAIN: + validateRuleChain(currentUser, entityId, callback); + return; + case CUSTOMER: + validateCustomer(currentUser, entityId, callback); + return; + case TENANT: + validateTenant(currentUser, entityId, callback); + return; + default: + //TODO: add support of other entities + throw new IllegalStateException("Not Implemented!"); + } + } + + private void validateDevice(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isSystemAdmin()) { + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else { + ListenableFuture deviceFuture = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); + Futures.addCallback(deviceFuture, getCallback(callback, device -> { + if (device == null) { + return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND); + } else { + if (!device.getTenantId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); + } else if (currentUser.isCustomerUser() && !device.getCustomerId().equals(currentUser.getCustomerId())) { + return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); + } else { + return ValidationResult.ok(device); + } + } + }), executor); + } + } + + private void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isSystemAdmin()) { + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else { + ListenableFuture assetFuture = assetService.findAssetByIdAsync(new AssetId(entityId.getId())); + Futures.addCallback(assetFuture, getCallback(callback, asset -> { + if (asset == null) { + return ValidationResult.entityNotFound("Asset with requested id wasn't found!"); + } else { + if (!asset.getTenantId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!"); + } else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) { + return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); + } else { + return ValidationResult.ok(asset); + } + } + }), executor); + } + } + + private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isCustomerUser()) { + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else { + ListenableFuture ruleChainFuture = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId())); + Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> { + if (ruleChain == null) { + return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!"); + } else { + if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); + } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { + return ValidationResult.accessDenied("Rule chain is not in system scope!"); + } else { + return ValidationResult.ok(ruleChain); + } + } + }), executor); + } + } + + private void validateRule(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isCustomerUser()) { + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else { + ListenableFuture ruleNodeFuture = ruleChainService.findRuleNodeByIdAsync(new RuleNodeId(entityId.getId())); + Futures.addCallback(ruleNodeFuture, getCallback(callback, ruleNodeTmp -> { + RuleNode ruleNode = ruleNodeTmp; + if (ruleNode == null) { + return ValidationResult.entityNotFound("Rule node with requested id wasn't found!"); + } else if (ruleNode.getRuleChainId() == null) { + return ValidationResult.entityNotFound("Rule chain with requested node id wasn't found!"); + } else { + //TODO: make async + RuleChain ruleChain = ruleChainService.findRuleChainById(ruleNode.getRuleChainId()); + if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); + } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { + return ValidationResult.accessDenied("Rule chain is not in system scope!"); + } else { + return ValidationResult.ok(ruleNode); + } + } + }), executor); + } + } + + private void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isSystemAdmin()) { + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else { + ListenableFuture customerFuture = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId())); + Futures.addCallback(customerFuture, getCallback(callback, customer -> { + if (customer == null) { + return ValidationResult.entityNotFound("Customer with requested id wasn't found!"); + } else { + if (!customer.getTenantId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!"); + } else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) { + return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); + } else { + return ValidationResult.ok(customer); + } + } + }), executor); + } + } + + private void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback callback) { + if (currentUser.isCustomerUser()) { + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); + } else if (currentUser.isSystemAdmin()) { + callback.onSuccess(ValidationResult.ok(null)); + } else { + ListenableFuture tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); + Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { + if (tenant == null) { + return ValidationResult.entityNotFound("Tenant with requested id wasn't found!"); + } else if (!tenant.getId().equals(currentUser.getTenantId())) { + return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); + } else { + return ValidationResult.ok(tenant); + } + }), executor); + } + } + + private FutureCallback getCallback(FutureCallback callback, Function> transformer) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable T result) { + callback.onSuccess(transformer.apply(result)); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + + public static void handleError(Throwable e, final DeferredResult response, HttpStatus defaultErrorStatus) { + ResponseEntity responseEntity; + if (e != null && e instanceof ToErrorResponseEntity) { + responseEntity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); + } else if (e != null && e instanceof IllegalArgumentException) { + responseEntity = new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } else { + responseEntity = new ResponseEntity<>(defaultErrorStatus); + } + response.setResult(responseEntity); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java b/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java similarity index 59% rename from application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java rename to application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java index f856ed6a10..81ab9b4eb6 100644 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationCallback.java +++ b/application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java @@ -13,34 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.plugin; +package org.thingsboard.server.service.security; -import com.hazelcast.util.function.Consumer; -import org.thingsboard.server.extensions.api.exception.AccessDeniedException; -import org.thingsboard.server.extensions.api.exception.EntityNotFoundException; -import org.thingsboard.server.extensions.api.exception.InternalErrorException; -import org.thingsboard.server.extensions.api.exception.UnauthorizedException; -import org.thingsboard.server.extensions.api.plugins.PluginCallback; -import org.thingsboard.server.extensions.api.plugins.PluginContext; +import com.google.common.util.concurrent.FutureCallback; +import org.thingsboard.server.service.telemetry.exception.AccessDeniedException; +import org.thingsboard.server.service.telemetry.exception.EntityNotFoundException; +import org.thingsboard.server.service.telemetry.exception.InternalErrorException; +import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; /** - * Created by ashvayka on 21.02.17. + * Created by ashvayka on 31.03.18. */ -public class ValidationCallback implements PluginCallback { +public class ValidationCallback implements FutureCallback { - private final PluginCallback callback; - private final Consumer action; + private final T response; + private final FutureCallback action; - public ValidationCallback(PluginCallback callback, Consumer action) { - this.callback = callback; + public ValidationCallback(T response, FutureCallback action) { + this.response = response; this.action = action; } @Override - public void onSuccess(PluginContext ctx, ValidationResult result) { + public void onSuccess(ValidationResult result) { ValidationResultCode resultCode = result.getResultCode(); if (resultCode == ValidationResultCode.OK) { - action.accept(ctx); + action.onSuccess(response); } else { Exception e; switch (resultCode) { @@ -60,12 +58,12 @@ public class ValidationCallback implements PluginCallback { e = new UnauthorizedException("Permission denied."); break; } - onFailure(ctx, e); + onFailure(e); } } @Override - public void onFailure(PluginContext ctx, Exception e) { - callback.onFailure(ctx, e); + public void onFailure(Throwable e) { + action.onFailure(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/ValidationResult.java b/application/src/main/java/org/thingsboard/server/service/security/ValidationResult.java new file mode 100644 index 0000000000..5f828a77d4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/ValidationResult.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2018 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.security; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ValidationResult { + + private final ValidationResultCode resultCode; + private final String message; + private final V v; + + public static ValidationResult ok(V v) { + return new ValidationResult<>(ValidationResultCode.OK, "Ok", v); + } + + public static ValidationResult accessDenied(String message) { + return new ValidationResult<>(ValidationResultCode.ACCESS_DENIED, message, null); + } + + public static ValidationResult entityNotFound(String message) { + return new ValidationResult<>(ValidationResultCode.ENTITY_NOT_FOUND, message, null); + } + + public static ValidationResult unauthorized(String message) { + return new ValidationResult<>(ValidationResultCode.UNAUTHORIZED, message, null); + } + + public static ValidationResult internalError(String message) { + return new ValidationResult<>(ValidationResultCode.INTERNAL_ERROR, message, null); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResultCode.java b/application/src/main/java/org/thingsboard/server/service/security/ValidationResultCode.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResultCode.java rename to application/src/main/java/org/thingsboard/server/service/security/ValidationResultCode.java index 2fd93ecd18..e7d016caf0 100644 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/ValidationResultCode.java +++ b/application/src/main/java/org/thingsboard/server/service/security/ValidationResultCode.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.plugin; +package org.thingsboard.server.service.security; +/** + * Created by ashvayka on 17.05.18. + */ public enum ValidationResultCode { OK, UNAUTHORIZED, diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java index a46fb48840..527e2bed83 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java @@ -15,24 +15,16 @@ */ package org.thingsboard.server.service.security.auth.jwt; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; -import org.thingsboard.server.config.JwtSettings; import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import java.util.List; -import java.util.stream.Collectors; - @Component @SuppressWarnings("unchecked") public class JwtAuthenticationProvider implements AuthenticationProvider { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java index f4da3a539c..7008662ecd 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java @@ -23,7 +23,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.thingsboard.server.config.ThingsboardSecurityConfiguration; import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java index 08de9ef39d..2e68b35cfa 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java @@ -16,7 +16,10 @@ package org.thingsboard.server.service.security.auth.jwt; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.*; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -26,7 +29,6 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java index a016f3ad03..fff15f295d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java @@ -18,8 +18,6 @@ package org.thingsboard.server.service.security.auth.jwt; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java index 36f2199ba3..195208a931 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java @@ -16,7 +16,11 @@ package org.thingsboard.server.service.security.auth.rest; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.*; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -27,7 +31,6 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java index 4f6a87ce07..03c1b34fa3 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java @@ -18,8 +18,6 @@ package org.thingsboard.server.service.security.auth.rest; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java index 5f8488e39c..2bbeddf053 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java @@ -18,8 +18,6 @@ package org.thingsboard.server.service.security.auth.rest; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java index db6a33669f..544b6fdea4 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/RawAccessJwtToken.java @@ -15,7 +15,13 @@ */ package org.thingsboard.server.service.security.model.token; -import io.jsonwebtoken.*; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SignatureException; +import io.jsonwebtoken.UnsupportedJwtException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.BadCredentialsException; diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java new file mode 100644 index 0000000000..45b969669c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -0,0 +1,386 @@ +/** + * Copyright © 2016-2018 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.state; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Function; +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.ListeningScheduledExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.actors.service.ActorService; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT; +import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT; +import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT; +import static org.thingsboard.server.common.data.DataConstants.INACTIVITY_EVENT; + +/** + * Created by ashvayka on 01.05.18. + */ +@Service +@Slf4j +//TODO: refactor to use page links as cursor and not fetch all +public class DefaultDeviceStateService implements DeviceStateService { + + private static final ObjectMapper json = new ObjectMapper(); + public static final String ACTIVITY_STATE = "active"; + public static final String LAST_CONNECT_TIME = "lastConnectTime"; + public static final String LAST_DISCONNECT_TIME = "lastDisconnectTime"; + public static final String LAST_ACTIVITY_TIME = "lastActivityTime"; + public static final String INACTIVITY_ALARM_TIME = "inactivityAlarmTime"; + public static final String INACTIVITY_TIMEOUT = "inactivityTimeout"; + + public static final List PERSISTENT_ATTRIBUTES = Arrays.asList(ACTIVITY_STATE, LAST_CONNECT_TIME, LAST_DISCONNECT_TIME, LAST_ACTIVITY_TIME, INACTIVITY_ALARM_TIME, INACTIVITY_TIMEOUT); + + @Autowired + private TenantService tenantService; + + @Autowired + private DeviceService deviceService; + + @Autowired + private AttributesService attributesService; + + @Autowired + private ActorService actorService; + + @Autowired + private TelemetrySubscriptionService tsSubService; + + @Value("${state.defaultInactivityTimeoutInSec}") + @Getter + private long defaultInactivityTimeoutInSec; + + @Value("${state.defaultStateCheckIntervalInSec}") + @Getter + private long defaultStateCheckIntervalInSec; + +// TODO in v2.1 +// @Value("${state.defaultStatePersistenceIntervalInSec}") +// @Getter +// private long defaultStatePersistenceIntervalInSec; +// +// @Value("${state.defaultStatePersistencePack}") +// @Getter +// private long defaultStatePersistencePack; + + private ListeningScheduledExecutorService queueExecutor; + + private ConcurrentMap> tenantDevices = new ConcurrentHashMap<>(); + private ConcurrentMap deviceStates = new ConcurrentHashMap<>(); + + @PostConstruct + public void init() { + // Should be always single threaded due to absence of locks. + queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor()); + queueExecutor.submit(this::initStateFromDB); + queueExecutor.scheduleAtFixedRate(this::updateState, defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS); + //TODO: schedule persistence in v2.1; + } + + @PreDestroy + public void stop() { + if (queueExecutor != null) { + queueExecutor.shutdownNow(); + } + } + + @Override + public void onDeviceAdded(Device device) { + queueExecutor.submit(() -> onDeviceAddedSync(device)); + } + + @Override + public void onDeviceUpdated(Device device) { + queueExecutor.submit(() -> onDeviceUpdatedSync(device)); + } + + @Override + public void onDeviceConnect(DeviceId deviceId) { + queueExecutor.submit(() -> onDeviceConnectSync(deviceId)); + } + + @Override + public void onDeviceActivity(DeviceId deviceId) { + queueExecutor.submit(() -> onDeviceActivitySync(deviceId)); + } + + @Override + public void onDeviceDisconnect(DeviceId deviceId) { + queueExecutor.submit(() -> onDeviceDisconnectSync(deviceId)); + } + + @Override + public void onDeviceDeleted(Device device) { + queueExecutor.submit(() -> onDeviceDeleted(device.getTenantId(), device.getId())); + } + + @Override + public void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) { + queueExecutor.submit(() -> onInactivityTimeoutUpdate(deviceId, inactivityTimeout)); + } + + @Override + public Optional getDeviceState(DeviceId deviceId) { + DeviceStateData state = deviceStates.get(deviceId); + if (state != null) { + return Optional.of(state.getState()); + } else { + return Optional.empty(); + } + } + + private void initStateFromDB() { + List tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData(); + for (Tenant tenant : tenants) { + List> fetchFutures = new ArrayList<>(); + List devices = deviceService.findDevicesByTenantId(tenant.getId(), new TextPageLink(Integer.MAX_VALUE)).getData(); + for (Device device : devices) { + fetchFutures.add(fetchDeviceState(device)); + } + try { + Futures.successfulAsList(fetchFutures).get().forEach(this::addDeviceUsingState); + } catch (InterruptedException | ExecutionException e) { + log.warn("Failed to init device state service from DB", e); + } + } + } + + private void addDeviceUsingState(DeviceStateData state) { + tenantDevices.computeIfAbsent(state.getTenantId(), id -> ConcurrentHashMap.newKeySet()).add(state.getDeviceId()); + deviceStates.put(state.getDeviceId(), state); + } + + private void updateState() { + long ts = System.currentTimeMillis(); + Set deviceIds = new HashSet<>(deviceStates.keySet()); + for (DeviceId deviceId : deviceIds) { + DeviceStateData stateData = deviceStates.get(deviceId); + DeviceState state = stateData.getState(); + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout()); + if (!state.isActive() && state.getLastInactivityAlarmTime() < state.getLastActivityTime()) { + state.setLastInactivityAlarmTime(ts); + pushRuleEngineMessage(stateData, INACTIVITY_EVENT); + saveAttribute(deviceId, INACTIVITY_ALARM_TIME, ts); + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); + } + } + } + + private void onDeviceConnectSync(DeviceId deviceId) { + DeviceStateData stateData = deviceStates.get(deviceId); + if (stateData != null) { + long ts = System.currentTimeMillis(); + stateData.getState().setLastConnectTime(ts); + pushRuleEngineMessage(stateData, CONNECT_EVENT); + saveAttribute(deviceId, LAST_CONNECT_TIME, ts); + } + } + + private void onDeviceDisconnectSync(DeviceId deviceId) { + DeviceStateData stateData = deviceStates.get(deviceId); + if (stateData != null) { + long ts = System.currentTimeMillis(); + stateData.getState().setLastDisconnectTime(ts); + pushRuleEngineMessage(stateData, DISCONNECT_EVENT); + saveAttribute(deviceId, LAST_DISCONNECT_TIME, ts); + } + } + + private void onDeviceActivitySync(DeviceId deviceId) { + DeviceStateData stateData = deviceStates.get(deviceId); + if (stateData != null) { + DeviceState state = stateData.getState(); + long ts = System.currentTimeMillis(); + state.setActive(true); + stateData.getState().setLastActivityTime(ts); + pushRuleEngineMessage(stateData, ACTIVITY_EVENT); + saveAttribute(deviceId, LAST_ACTIVITY_TIME, ts); + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); + } + } + + private void onInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) { + if (inactivityTimeout == 0L) { + return; + } + DeviceStateData stateData = deviceStates.get(deviceId); + if (stateData != null) { + long ts = System.currentTimeMillis(); + DeviceState state = stateData.getState(); + state.setInactivityTimeout(inactivityTimeout); + boolean oldActive = state.isActive(); + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout()); + if (!oldActive && state.isActive() || oldActive && !state.isActive()) { + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive()); + } + } + } + + private void onDeviceAddedSync(Device device) { + Futures.addCallback(fetchDeviceState(device), new FutureCallback() { + @Override + public void onSuccess(@Nullable DeviceStateData state) { + addDeviceUsingState(state); + } + + @Override + public void onFailure(Throwable t) { + log.warn("Failed to register device to the state service", t); + } + }); + } + + private void onDeviceUpdatedSync(Device device) { + DeviceStateData stateData = deviceStates.get(device.getId()); + if (stateData != null) { + TbMsgMetaData md = new TbMsgMetaData(); + md.putValue("deviceName", device.getName()); + md.putValue("deviceType", device.getType()); + stateData.setMetaData(md); + } + } + + private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) { + deviceStates.remove(deviceId); + Set deviceIds = tenantDevices.get(tenantId); + if (deviceIds != null) { + deviceIds.remove(deviceId); + if (deviceIds.isEmpty()) { + tenantDevices.remove(tenantId); + } + } + } + + private ListenableFuture fetchDeviceState(Device device) { + ListenableFuture> attributes = attributesService.find(device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES); + return Futures.transform(attributes, new Function, DeviceStateData>() { + @Nullable + @Override + public DeviceStateData apply(@Nullable List attributes) { + long lastActivityTime = getAttributeValue(attributes, LAST_ACTIVITY_TIME, 0L); + long inactivityAlarmTime = getAttributeValue(attributes, INACTIVITY_ALARM_TIME, 0L); + long inactivityTimeout = getAttributeValue(attributes, INACTIVITY_TIMEOUT, TimeUnit.SECONDS.toMillis(defaultInactivityTimeoutInSec)); + boolean active = System.currentTimeMillis() < lastActivityTime + inactivityTimeout; + DeviceState deviceState = DeviceState.builder() + .active(active) + .lastConnectTime(getAttributeValue(attributes, LAST_CONNECT_TIME, 0L)) + .lastDisconnectTime(getAttributeValue(attributes, LAST_DISCONNECT_TIME, 0L)) + .lastActivityTime(lastActivityTime) + .lastInactivityAlarmTime(inactivityAlarmTime) + .inactivityTimeout(inactivityTimeout) + .build(); + TbMsgMetaData md = new TbMsgMetaData(); + md.putValue("deviceName", device.getName()); + md.putValue("deviceType", device.getType()); + return DeviceStateData.builder() + .tenantId(device.getTenantId()) + .deviceId(device.getId()) + .metaData(md) + .state(deviceState).build(); + } + }); + } + + private long getAttributeValue(List attributes, String attributeName, long defaultValue) { + for (AttributeKvEntry attribute : attributes) { + if (attribute.getKey().equals(attributeName)) { + return attribute.getLongValue().orElse(defaultValue); + } + } + return defaultValue; + } + + private void pushRuleEngineMessage(DeviceStateData stateData, String msgType) { + DeviceState state = stateData.getState(); + try { + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON + , json.writeValueAsString(state) + , null, null, 0L); + actorService.onMsg(new ServiceToRuleEngineMsg(stateData.getTenantId(), tbMsg)); + } catch (Exception e) { + log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); + } + } + + private void saveAttribute(DeviceId deviceId, String key, long value) { + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); + } + + private void saveAttribute(DeviceId deviceId, String key, boolean value) { + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value)); + } + + private class AttributeSaveCallback implements FutureCallback { + private final DeviceId deviceId; + private final String key; + private final Object value; + + AttributeSaveCallback(DeviceId deviceId, String key, Object value) { + this.deviceId = deviceId; + this.key = key; + this.value = value; + } + + @Override + public void onSuccess(@Nullable Void result) { + log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/state/DeviceState.java b/application/src/main/java/org/thingsboard/server/service/state/DeviceState.java new file mode 100644 index 0000000000..29167ee519 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/state/DeviceState.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2018 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.state; + +import lombok.Builder; +import lombok.Data; + +/** + * Created by ashvayka on 01.05.18. + */ +@Data +@Builder +public class DeviceState { + + private boolean active; + private long lastConnectTime; + private long lastActivityTime; + private long lastDisconnectTime; + private long lastInactivityAlarmTime; + private long inactivityTimeout; + +} diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/messaging/PendingRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java similarity index 68% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/messaging/PendingRpcRequestMetadata.java rename to application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java index 9937ea5308..cb9eeeaea7 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/messaging/PendingRpcRequestMetadata.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java @@ -13,25 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.messaging; +package org.thingsboard.server.service.state; +import lombok.Builder; import lombok.Data; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; - -import java.util.UUID; +import org.thingsboard.server.common.msg.TbMsgMetaData; /** - * @author Andrew Shvayka + * Created by ashvayka on 01.05.18. */ @Data -class PendingRpcRequestMetadata { - private final UUID uid; - private final int requestId; +@Builder +class DeviceStateData { + private final TenantId tenantId; - private final RuleId ruleId; - private final CustomerId customerId; private final DeviceId deviceId; + + private TbMsgMetaData metaData; + private final DeviceState state; + } diff --git a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/action/RabbitMqActionMsg.java b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java similarity index 52% rename from extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/action/RabbitMqActionMsg.java rename to application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java index bb932c8b5d..37f785cf44 100644 --- a/extensions/extension-rabbitmq/src/main/java/org/thingsboard/server/extensions/rabbitmq/action/RabbitMqActionMsg.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java @@ -13,19 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.rabbitmq.action; +package org.thingsboard.server.service.state; -import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg; + +import java.util.Optional; /** - * @author Andrew Shvayka + * Created by ashvayka on 01.05.18. */ -public class RabbitMqActionMsg extends AbstractRuleToPluginMsg { +public interface DeviceStateService { + + void onDeviceAdded(Device device); + + void onDeviceUpdated(Device device); + + void onDeviceDeleted(Device device); + + void onDeviceConnect(DeviceId deviceId); + + void onDeviceActivity(DeviceId deviceId); + + void onDeviceDisconnect(DeviceId deviceId); + + void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout); + + Optional getDeviceState(DeviceId deviceId); - public RabbitMqActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, RabbitMqActionPayload payload) { - super(tenantId, customerId, deviceId, payload); - } } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/AttributeData.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java similarity index 95% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/AttributeData.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java index e4b73bc572..d5018167dd 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/AttributeData.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AttributeData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry; +package org.thingsboard.server.service.telemetry; public class AttributeData implements Comparable{ diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java new file mode 100644 index 0000000000..0942b4010a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -0,0 +1,710 @@ +/** + * Copyright © 2016-2018 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.telemetry; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.thingsboard.rule.engine.api.util.DonAsynchron; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvQuery; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.gen.cluster.ClusterAPIProtos; +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; +import org.thingsboard.server.service.state.DefaultDeviceStateService; +import org.thingsboard.server.service.state.DeviceStateService; +import org.thingsboard.server.service.telemetry.sub.Subscription; +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.telemetry.sub.SubscriptionState; +import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Created by ashvayka on 27.03.18. + */ +@Service +@Slf4j +public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptionService { + + @Autowired + private TelemetryWebSocketService wsService; + + @Autowired + private AttributesService attrService; + + @Autowired + private TimeseriesService tsService; + + @Autowired + private ClusterRoutingService routingService; + + @Autowired + private ClusterRpcService rpcService; + + @Autowired + @Lazy + private DeviceStateService stateService; + + private ExecutorService tsCallBackExecutor; + private ExecutorService wsCallBackExecutor; + + @PostConstruct + public void initExecutor() { + tsCallBackExecutor = Executors.newSingleThreadExecutor(); + wsCallBackExecutor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (tsCallBackExecutor != null) { + tsCallBackExecutor.shutdownNow(); + } + if (wsCallBackExecutor != null) { + wsCallBackExecutor.shutdownNow(); + } + } + + private final Map> subscriptionsByEntityId = new HashMap<>(); + private final Map> subscriptionsByWsSessionId = new HashMap<>(); + + @Override + public void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub) { + Optional server = routingService.resolveById(entityId); + Subscription subscription; + if (server.isPresent()) { + ServerAddress address = server.get(); + log.trace("[{}] Forwarding subscription [{}] for device [{}] to [{}]", sessionId, sub.getSubscriptionId(), entityId, address); + subscription = new Subscription(sub, true, address); + tellNewSubscription(address, sessionId, subscription); + } else { + log.trace("[{}] Registering local subscription [{}] for device [{}]", sessionId, sub.getSubscriptionId(), entityId); + subscription = new Subscription(sub, true); + } + registerSubscription(sessionId, entityId, subscription); + } + + @Override + public void cleanupLocalWsSessionSubscriptions(TelemetryWebSocketSessionRef sessionRef, String sessionId) { + cleanupLocalWsSessionSubscriptions(sessionId); + } + + @Override + public void removeSubscription(String sessionId, int subscriptionId) { + log.debug("[{}][{}] Going to remove subscription.", sessionId, subscriptionId); + Map sessionSubscriptions = subscriptionsByWsSessionId.get(sessionId); + if (sessionSubscriptions != null) { + Subscription subscription = sessionSubscriptions.remove(subscriptionId); + if (subscription != null) { + processSubscriptionRemoval(sessionId, sessionSubscriptions, subscription); + } else { + log.debug("[{}][{}] Subscription not found!", sessionId, subscriptionId); + } + } else { + log.debug("[{}] No session subscriptions found!", sessionId); + } + } + + @Override + public void saveAndNotify(EntityId entityId, List ts, FutureCallback callback) { + saveAndNotify(entityId, ts, 0L, callback); + } + + @Override + public void saveAndNotify(EntityId entityId, List ts, long ttl, FutureCallback callback) { + ListenableFuture> saveFuture = tsService.save(entityId, ts, ttl); + addMainCallback(saveFuture, callback); + addWsCallback(saveFuture, success -> onTimeseriesUpdate(entityId, ts)); + } + + @Override + public void saveAndNotify(EntityId entityId, String scope, List attributes, FutureCallback callback) { + ListenableFuture> saveFuture = attrService.save(entityId, scope, attributes); + addMainCallback(saveFuture, callback); + addWsCallback(saveFuture, success -> onAttributesUpdate(entityId, scope, attributes)); + } + + @Override + public void saveAttrAndNotify(EntityId entityId, String scope, String key, long value, FutureCallback callback) { + saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(key, value) + , System.currentTimeMillis())), callback); + } + + @Override + public void saveAttrAndNotify(EntityId entityId, String scope, String key, String value, FutureCallback callback) { + saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry(key, value) + , System.currentTimeMillis())), callback); + } + + @Override + public void saveAttrAndNotify(EntityId entityId, String scope, String key, double value, FutureCallback callback) { + saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new DoubleDataEntry(key, value) + , System.currentTimeMillis())), callback); + } + + @Override + public void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback callback) { + saveAndNotify(entityId, scope, Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value) + , System.currentTimeMillis())), callback); + } + + @Override + public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.SubscriptionProto proto; + try { + proto = ClusterAPIProtos.SubscriptionProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + Map statesMap = proto.getKeyStatesList().stream().collect( + Collectors.toMap(ClusterAPIProtos.SubscriptionKetStateProto::getKey, ClusterAPIProtos.SubscriptionKetStateProto::getTs)); + Subscription subscription = new Subscription( + new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(), + EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), + TelemetryFeature.valueOf(proto.getType()), proto.getAllKeys(), statesMap, proto.getScope()), + false, new ServerAddress(serverAddress.getHost(), serverAddress.getPort())); + + addRemoteWsSubscription(serverAddress, proto.getSessionId(), subscription); + } + + @Override + public void onRemoteSubscriptionUpdate(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.SubscriptionUpdateProto proto; + try { + proto = ClusterAPIProtos.SubscriptionUpdateProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + SubscriptionUpdate update = convert(proto); + String sessionId = proto.getSessionId(); + log.trace("[{}] Processing remote subscription onUpdate [{}]", sessionId, update); + Optional subOpt = getSubscription(sessionId, update.getSubscriptionId()); + if (subOpt.isPresent()) { + updateSubscriptionState(sessionId, subOpt.get(), update); + wsService.sendWsMsg(sessionId, update); + } + } + + @Override + public void onRemoteSubscriptionClose(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.SubscriptionCloseProto proto; + try { + proto = ClusterAPIProtos.SubscriptionCloseProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + removeSubscription(proto.getSessionId(), proto.getSubscriptionId()); + } + + @Override + public void onRemoteSessionClose(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.SessionCloseProto proto; + try { + proto = ClusterAPIProtos.SessionCloseProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + cleanupRemoteWsSessionSubscriptions(proto.getSessionId()); + } + + @Override + public void onRemoteAttributesUpdate(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.AttributeUpdateProto proto; + try { + proto = ClusterAPIProtos.AttributeUpdateProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + onAttributesUpdate(EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), proto.getScope(), + proto.getDataList().stream().map(this::toAttribute).collect(Collectors.toList())); + } + + @Override + public void onRemoteTsUpdate(ServerAddress serverAddress, byte[] data) { + ClusterAPIProtos.TimeseriesUpdateProto proto; + try { + proto = ClusterAPIProtos.TimeseriesUpdateProto.parseFrom(data); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + onTimeseriesUpdate(EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), + proto.getDataList().stream().map(this::toTimeseries).collect(Collectors.toList())); + } + + @Override + public void onClusterUpdate() { + log.trace("Processing cluster onUpdate msg!"); + Iterator>> deviceIterator = subscriptionsByEntityId.entrySet().iterator(); + while (deviceIterator.hasNext()) { + Map.Entry> e = deviceIterator.next(); + Set subscriptions = e.getValue(); + Optional newAddressOptional = routingService.resolveById(e.getKey()); + if (newAddressOptional.isPresent()) { + newAddressOptional.ifPresent(serverAddress -> checkSubsciptionsNewAddress(serverAddress, subscriptions)); + } else { + checkSubsciptionsPrevAddress(subscriptions); + } + if (subscriptions.size() == 0) { + log.trace("[{}] No more subscriptions for this device on current server.", e.getKey()); + deviceIterator.remove(); + } + } + } + + private void checkSubsciptionsNewAddress(ServerAddress newAddress, Set subscriptions) { + Iterator subscriptionIterator = subscriptions.iterator(); + while (subscriptionIterator.hasNext()) { + Subscription s = subscriptionIterator.next(); + if (s.isLocal()) { + if (!newAddress.equals(s.getServer())) { + log.trace("[{}] Local subscription is now handled on new server [{}]", s.getWsSessionId(), newAddress); + s.setServer(newAddress); + tellNewSubscription(newAddress, s.getWsSessionId(), s); + } + } else { + log.trace("[{}] Remote subscription is now handled on new server address: [{}]", s.getWsSessionId(), newAddress); + subscriptionIterator.remove(); + //TODO: onUpdate state of subscription by WsSessionId and other maps. + } + } + } + + private void checkSubsciptionsPrevAddress(Set subscriptions) { + for (Subscription s : subscriptions) { + if (s.isLocal() && s.getServer() != null) { + log.trace("[{}] Local subscription is no longer handled on remote server address [{}]", s.getWsSessionId(), s.getServer()); + s.setServer(null); + } else { + log.trace("[{}] Remote subscription is on up to date server address.", s.getWsSessionId()); + } + } + } + + private void addRemoteWsSubscription(ServerAddress address, String sessionId, Subscription subscription) { + EntityId entityId = subscription.getEntityId(); + log.trace("[{}] Registering remote subscription [{}] for device [{}] to [{}]", sessionId, subscription.getSubscriptionId(), entityId, address); + registerSubscription(sessionId, entityId, subscription); + if (subscription.getType() == TelemetryFeature.ATTRIBUTES) { + final Map keyStates = subscription.getKeyStates(); + DonAsynchron.withCallback(attrService.find(entityId, DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> { + List missedUpdates = new ArrayList<>(); + values.forEach(latestEntry -> { + if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) { + missedUpdates.add(new BasicTsKvEntry(latestEntry.getLastUpdateTs(), latestEntry)); + } + }); + if (!missedUpdates.isEmpty()) { + tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates)); + } + }, + e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); + } else if (subscription.getType() == TelemetryFeature.TIMESERIES) { + long curTs = System.currentTimeMillis(); + List queries = new ArrayList<>(); + subscription.getKeyStates().entrySet().forEach(e -> { + queries.add(new BaseTsKvQuery(e.getKey(), e.getValue() + 1L, curTs)); + }); + + DonAsynchron.withCallback(tsService.findAll(entityId, queries), + missedUpdates -> { + if (!missedUpdates.isEmpty()) { + tellRemoteSubUpdate(address, sessionId, new SubscriptionUpdate(subscription.getSubscriptionId(), missedUpdates)); + } + }, + e -> log.error("Failed to fetch missed updates.", e), + tsCallBackExecutor); + } + } + + private void onAttributesUpdate(EntityId entityId, String scope, List attributes) { + Optional serverAddress = routingService.resolveById(entityId); + if (!serverAddress.isPresent()) { + onLocalAttributesUpdate(entityId, scope, attributes); + if (entityId.getEntityType() == EntityType.DEVICE && DataConstants.SERVER_SCOPE.equalsIgnoreCase(scope)) { + for (AttributeKvEntry attribute : attributes) { + if (attribute.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) { + stateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), attribute.getLongValue().orElse(0L)); + } + } + } + } else { + tellRemoteAttributesUpdate(serverAddress.get(), entityId, scope, attributes); + } + } + + private void onTimeseriesUpdate(EntityId entityId, List ts) { + Optional serverAddress = routingService.resolveById(entityId); + if (!serverAddress.isPresent()) { + onLocalTimeseriesUpdate(entityId, ts); + } else { + tellRemoteTimeseriesUpdate(serverAddress.get(), entityId, ts); + } + } + + private void onLocalAttributesUpdate(EntityId entityId, String scope, List attributes) { + onLocalSubUpdate(entityId, s -> TelemetryFeature.ATTRIBUTES == s.getType() && (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope())), s -> { + List subscriptionUpdate = null; + for (AttributeKvEntry kv : attributes) { + if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) { + if (subscriptionUpdate == null) { + subscriptionUpdate = new ArrayList<>(); + } + subscriptionUpdate.add(new BasicTsKvEntry(kv.getLastUpdateTs(), kv)); + } + } + return subscriptionUpdate; + }); + } + + private void onLocalTimeseriesUpdate(EntityId entityId, List ts) { + onLocalSubUpdate(entityId, s -> TelemetryFeature.TIMESERIES == s.getType(), s -> { + List subscriptionUpdate = null; + for (TsKvEntry kv : ts) { + if (s.isAllKeys() || s.getKeyStates().containsKey((kv.getKey()))) { + if (subscriptionUpdate == null) { + subscriptionUpdate = new ArrayList<>(); + } + subscriptionUpdate.add(kv); + } + } + return subscriptionUpdate; + }); + } + + private void onLocalSubUpdate(EntityId entityId, Predicate filter, Function> f) { + Set deviceSubscriptions = subscriptionsByEntityId.get(entityId); + if (deviceSubscriptions != null) { + deviceSubscriptions.stream().filter(filter).forEach(s -> { + String sessionId = s.getWsSessionId(); + List subscriptionUpdate = f.apply(s); + if (subscriptionUpdate == null || !subscriptionUpdate.isEmpty()) { + SubscriptionUpdate update = new SubscriptionUpdate(s.getSubscriptionId(), subscriptionUpdate); + if (s.isLocal()) { + updateSubscriptionState(sessionId, s, update); + wsService.sendWsMsg(sessionId, update); + } else { + tellRemoteSubUpdate(s.getServer(), sessionId, update); + } + } + }); + } else { + log.debug("[{}] No device subscriptions to process!", entityId); + } + } + + private void updateSubscriptionState(String sessionId, Subscription subState, SubscriptionUpdate update) { + log.trace("[{}] updating subscription state {} using onUpdate {}", sessionId, subState, update); + update.getLatestValues().entrySet().forEach(e -> subState.setKeyState(e.getKey(), e.getValue())); + } + + private void registerSubscription(String sessionId, EntityId entityId, Subscription subscription) { + Set deviceSubscriptions = subscriptionsByEntityId.computeIfAbsent(entityId, k -> new HashSet<>()); + deviceSubscriptions.add(subscription); + Map sessionSubscriptions = subscriptionsByWsSessionId.computeIfAbsent(sessionId, k -> new HashMap<>()); + sessionSubscriptions.put(subscription.getSubscriptionId(), subscription); + } + + private void cleanupLocalWsSessionSubscriptions(String sessionId) { + cleanupWsSessionSubscriptions(sessionId, true); + } + + private void cleanupRemoteWsSessionSubscriptions(String sessionId) { + cleanupWsSessionSubscriptions(sessionId, false); + } + + private void cleanupWsSessionSubscriptions(String sessionId, boolean localSession) { + log.debug("[{}] Removing all subscriptions for particular session.", sessionId); + Map sessionSubscriptions = subscriptionsByWsSessionId.get(sessionId); + if (sessionSubscriptions != null) { + int sessionSubscriptionSize = sessionSubscriptions.size(); + + for (Subscription subscription : sessionSubscriptions.values()) { + EntityId entityId = subscription.getEntityId(); + Set deviceSubscriptions = subscriptionsByEntityId.get(entityId); + deviceSubscriptions.remove(subscription); + if (deviceSubscriptions.isEmpty()) { + subscriptionsByEntityId.remove(entityId); + } + } + subscriptionsByWsSessionId.remove(sessionId); + log.debug("[{}] Removed {} subscriptions for particular session.", sessionId, sessionSubscriptionSize); + + if (localSession) { + notifyWsSubscriptionClosed(sessionId, sessionSubscriptions); + } + } else { + log.debug("[{}] No subscriptions found!", sessionId); + } + } + + private void notifyWsSubscriptionClosed(String sessionId, Map sessionSubscriptions) { + Set affectedServers = new HashSet<>(); + for (Subscription subscription : sessionSubscriptions.values()) { + if (subscription.getServer() != null) { + affectedServers.add(subscription.getServer()); + } + } + for (ServerAddress address : affectedServers) { + log.debug("[{}] Going to onSubscriptionUpdate [{}] server about session close event", sessionId, address); + tellRemoteSessionClose(address, sessionId); + } + } + + private void processSubscriptionRemoval(String sessionId, Map sessionSubscriptions, Subscription subscription) { + EntityId entityId = subscription.getEntityId(); + if (subscription.isLocal() && subscription.getServer() != null) { + tellRemoteSubClose(subscription.getServer(), sessionId, subscription.getSubscriptionId()); + } + if (sessionSubscriptions.isEmpty()) { + log.debug("[{}] Removed last subscription for particular session.", sessionId); + subscriptionsByWsSessionId.remove(sessionId); + } else { + log.debug("[{}] Removed session subscription.", sessionId); + } + Set deviceSubscriptions = subscriptionsByEntityId.get(entityId); + if (deviceSubscriptions != null) { + boolean result = deviceSubscriptions.remove(subscription); + if (result) { + if (deviceSubscriptions.size() == 0) { + log.debug("[{}] Removed last subscription for particular device.", sessionId); + subscriptionsByEntityId.remove(entityId); + } else { + log.debug("[{}] Removed device subscription.", sessionId); + } + } else { + log.debug("[{}] Subscription not found!", sessionId); + } + } else { + log.debug("[{}] No device subscriptions found!", sessionId); + } + } + + private void addMainCallback(ListenableFuture> saveFuture, final FutureCallback callback) { + Futures.addCallback(saveFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List result) { + callback.onSuccess(null); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }, tsCallBackExecutor); + } + + private void addWsCallback(ListenableFuture> saveFuture, Consumer callback) { + Futures.addCallback(saveFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List result) { + callback.accept(null); + } + + @Override + public void onFailure(Throwable t) { + } + }, wsCallBackExecutor); + } + + private void tellNewSubscription(ServerAddress address, String sessionId, Subscription sub) { + ClusterAPIProtos.SubscriptionProto.Builder builder = ClusterAPIProtos.SubscriptionProto.newBuilder(); + builder.setSessionId(sessionId); + builder.setSubscriptionId(sub.getSubscriptionId()); + builder.setEntityType(sub.getEntityId().getEntityType().name()); + builder.setEntityId(sub.getEntityId().getId().toString()); + builder.setType(sub.getType().name()); + builder.setAllKeys(sub.isAllKeys()); + builder.setScope(sub.getScope()); + sub.getKeyStates().entrySet().forEach(e -> builder.addKeyStates( + ClusterAPIProtos.SubscriptionKetStateProto.newBuilder().setKey(e.getKey()).setTs(e.getValue()).build())); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE, builder.build().toByteArray()); + } + + private void tellRemoteSubUpdate(ServerAddress address, String sessionId, SubscriptionUpdate update) { + ClusterAPIProtos.SubscriptionUpdateProto.Builder builder = ClusterAPIProtos.SubscriptionUpdateProto.newBuilder(); + builder.setSessionId(sessionId); + builder.setSubscriptionId(update.getSubscriptionId()); + builder.setErrorCode(update.getErrorCode()); + if (update.getErrorMsg() != null) { + builder.setErrorMsg(update.getErrorMsg()); + } + update.getData().entrySet().forEach( + e -> { + ClusterAPIProtos.SubscriptionUpdateValueListProto.Builder dataBuilder = ClusterAPIProtos.SubscriptionUpdateValueListProto.newBuilder(); + + dataBuilder.setKey(e.getKey()); + e.getValue().forEach(v -> { + Object[] array = (Object[]) v; + dataBuilder.addTs((long) array[0]); + dataBuilder.addValue((String) array[1]); + }); + + builder.addData(dataBuilder.build()); + } + ); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE, builder.build().toByteArray()); + } + + private void tellRemoteAttributesUpdate(ServerAddress address, EntityId entityId, String scope, List attributes) { + ClusterAPIProtos.AttributeUpdateProto.Builder builder = ClusterAPIProtos.AttributeUpdateProto.newBuilder(); + builder.setEntityId(entityId.getId().toString()); + builder.setEntityType(entityId.getEntityType().name()); + builder.setScope(scope); + attributes.forEach(v -> builder.addData(toKeyValueProto(v.getLastUpdateTs(), v).build())); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE, builder.build().toByteArray()); + } + + private void tellRemoteTimeseriesUpdate(ServerAddress address, EntityId entityId, List ts) { + ClusterAPIProtos.TimeseriesUpdateProto.Builder builder = ClusterAPIProtos.TimeseriesUpdateProto.newBuilder(); + builder.setEntityId(entityId.getId().toString()); + builder.setEntityType(entityId.getEntityType().name()); + ts.forEach(v -> builder.addData(toKeyValueProto(v.getTs(), v).build())); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE, builder.build().toByteArray()); + } + + private void tellRemoteSessionClose(ServerAddress address, String sessionId) { + ClusterAPIProtos.SessionCloseProto proto = ClusterAPIProtos.SessionCloseProto.newBuilder().setSessionId(sessionId).build(); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE, proto.toByteArray()); + } + + private void tellRemoteSubClose(ServerAddress address, String sessionId, int subscriptionId) { + ClusterAPIProtos.SubscriptionCloseProto proto = ClusterAPIProtos.SubscriptionCloseProto.newBuilder().setSessionId(sessionId).setSubscriptionId(subscriptionId).build(); + rpcService.tell(address, ClusterAPIProtos.MessageType.CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE, proto.toByteArray()); + } + + private ClusterAPIProtos.KeyValueProto.Builder toKeyValueProto(long ts, KvEntry attr) { + ClusterAPIProtos.KeyValueProto.Builder dataBuilder = ClusterAPIProtos.KeyValueProto.newBuilder(); + dataBuilder.setKey(attr.getKey()); + dataBuilder.setTs(ts); + dataBuilder.setValueType(attr.getDataType().ordinal()); + switch (attr.getDataType()) { + case BOOLEAN: + Optional booleanValue = attr.getBooleanValue(); + booleanValue.ifPresent(dataBuilder::setBoolValue); + break; + case LONG: + Optional longValue = attr.getLongValue(); + longValue.ifPresent(dataBuilder::setLongValue); + break; + case DOUBLE: + Optional doubleValue = attr.getDoubleValue(); + doubleValue.ifPresent(dataBuilder::setDoubleValue); + break; + case STRING: + Optional stringValue = attr.getStrValue(); + stringValue.ifPresent(dataBuilder::setStrValue); + break; + } + return dataBuilder; + } + + private AttributeKvEntry toAttribute(ClusterAPIProtos.KeyValueProto proto) { + return new BaseAttributeKvEntry(getKvEntry(proto), proto.getTs()); + } + + private TsKvEntry toTimeseries(ClusterAPIProtos.KeyValueProto proto) { + return new BasicTsKvEntry(proto.getTs(), getKvEntry(proto)); + } + + private KvEntry getKvEntry(ClusterAPIProtos.KeyValueProto proto) { + KvEntry entry = null; + DataType type = DataType.values()[proto.getValueType()]; + switch (type) { + case BOOLEAN: + entry = new BooleanDataEntry(proto.getKey(), proto.getBoolValue()); + break; + case LONG: + entry = new LongDataEntry(proto.getKey(), proto.getLongValue()); + break; + case DOUBLE: + entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleValue()); + break; + case STRING: + entry = new StringDataEntry(proto.getKey(), proto.getStrValue()); + break; + } + return entry; + } + + private SubscriptionUpdate convert(ClusterAPIProtos.SubscriptionUpdateProto proto) { + if (proto.getErrorCode() > 0) { + return new SubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); + } else { + Map> data = new TreeMap<>(); + proto.getDataList().forEach(v -> { + List values = data.computeIfAbsent(v.getKey(), k -> new ArrayList<>()); + for (int i = 0; i < v.getTsCount(); i++) { + Object[] value = new Object[2]; + value[0] = v.getTs(i); + value[1] = v.getValue(i); + values.add(value); + } + }); + return new SubscriptionUpdate(proto.getSubscriptionId(), data); + } + } + + private Optional getSubscription(String sessionId, int subscriptionId) { + Subscription state = null; + Map subMap = subscriptionsByWsSessionId.get(sessionId); + if (subMap != null) { + state = subMap.get(subscriptionId); + } + return Optional.ofNullable(state); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java new file mode 100644 index 0000000000..3b2d69030e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -0,0 +1,560 @@ +/** + * Copyright © 2016-2018 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.telemetry; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Function; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvQuery; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.ValidationResult; +import org.thingsboard.server.service.telemetry.cmd.AttributesSubscriptionCmd; +import org.thingsboard.server.service.telemetry.cmd.GetHistoryCmd; +import org.thingsboard.server.service.telemetry.cmd.SubscriptionCmd; +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmd; +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.telemetry.cmd.TimeseriesSubscriptionCmd; +import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; +import org.thingsboard.server.service.telemetry.sub.SubscriptionState; +import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate; + +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * Created by ashvayka on 27.03.18. + */ +@Service +@Slf4j +public class DefaultTelemetryWebSocketService implements TelemetryWebSocketService { + + public static final int DEFAULT_LIMIT = 100; + public static final Aggregation DEFAULT_AGGREGATION = Aggregation.NONE; + private static final int UNKNOWN_SUBSCRIPTION_ID = 0; + private static final String PROCESSING_MSG = "[{}] Processing: {}"; + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final String FAILED_TO_FETCH_DATA = "Failed to fetch data!"; + private static final String FAILED_TO_FETCH_ATTRIBUTES = "Failed to fetch attributes!"; + private static final String SESSION_META_DATA_NOT_FOUND = "Session meta-data not found!"; + + private final ConcurrentMap wsSessionsMap = new ConcurrentHashMap<>(); + + @Autowired + private TelemetrySubscriptionService subscriptionManager; + + @Autowired + private TelemetryWebSocketMsgEndpoint msgEndpoint; + + @Autowired + private AccessValidator accessValidator; + + @Autowired + private AttributesService attributesService; + + @Autowired + private TimeseriesService tsService; + + private ExecutorService executor; + + @PostConstruct + public void initExecutor() { + executor = Executors.newSingleThreadExecutor(); + } + + @PreDestroy + public void shutdownExecutor() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @Override + public void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { + String sessionId = sessionRef.getSessionId(); + log.debug(PROCESSING_MSG, sessionId, event); + switch (event.getEventType()) { + case ESTABLISHED: + wsSessionsMap.put(sessionId, new WsSessionMetaData(sessionRef)); + break; + case ERROR: + log.debug("[{}] Unknown websocket session error: {}. ", sessionId, event.getError().orElse(null)); + break; + case CLOSED: + wsSessionsMap.remove(sessionId); + subscriptionManager.cleanupLocalWsSessionSubscriptions(sessionRef, sessionId); + break; + } + } + + @Override + public void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg) { + if (log.isTraceEnabled()) { + log.trace("[{}] Processing: {}", sessionRef.getSessionId(), msg); + } + + try { + TelemetryPluginCmdsWrapper cmdsWrapper = jsonMapper.readValue(msg, TelemetryPluginCmdsWrapper.class); + if (cmdsWrapper != null) { + if (cmdsWrapper.getAttrSubCmds() != null) { + cmdsWrapper.getAttrSubCmds().forEach(cmd -> handleWsAttributesSubscriptionCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getTsSubCmds() != null) { + cmdsWrapper.getTsSubCmds().forEach(cmd -> handleWsTimeseriesSubscriptionCmd(sessionRef, cmd)); + } + if (cmdsWrapper.getHistoryCmds() != null) { + cmdsWrapper.getHistoryCmds().forEach(cmd -> handleWsHistoryCmd(sessionRef, cmd)); + } + } + } catch (IOException e) { + log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); + SubscriptionUpdate update = new SubscriptionUpdate(UNKNOWN_SUBSCRIPTION_ID, SubscriptionErrorCode.INTERNAL_ERROR, SESSION_META_DATA_NOT_FOUND); + sendWsMsg(sessionRef, update); + } + } + + @Override + public void sendWsMsg(String sessionId, SubscriptionUpdate update) { + WsSessionMetaData md = wsSessionsMap.get(sessionId); + if (md != null) { + sendWsMsg(md.getSessionRef(), update); + } + } + + private void handleWsAttributesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd) { + String sessionId = sessionRef.getSessionId(); + log.debug("[{}] Processing: {}", sessionId, cmd); + + if (validateSessionMetadata(sessionRef, cmd, sessionId)) { + if (cmd.isUnsubscribe()) { + unsubscribe(sessionRef, cmd, sessionId); + } else if (validateSubscriptionCmd(sessionRef, cmd)) { + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); + log.debug("[{}] fetching latest attributes ({}) values for device: {}", sessionId, cmd.getKeys(), entityId); + Optional> keysOptional = getKeys(cmd); + if (keysOptional.isPresent()) { + List keys = new ArrayList<>(keysOptional.get()); + handleWsAttributesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId, keys); + } else { + handleWsAttributesSubscription(sessionRef, cmd, sessionId, entityId); + } + } + } + } + + private void handleWsAttributesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, + AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId, + List keys) { + FutureCallback> callback = new FutureCallback>() { + @Override + public void onSuccess(List data) { + List attributesData = data.stream().map(d -> new BasicTsKvEntry(d.getLastUpdateTs(), d)).collect(Collectors.toList()); + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), attributesData)); + + Map subState = new HashMap<>(keys.size()); + keys.forEach(key -> subState.put(key, 0L)); + attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); + + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.ATTRIBUTES, false, subState, cmd.getScope()); + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); + } + + @Override + public void onFailure(Throwable e) { + log.error(FAILED_TO_FETCH_ATTRIBUTES, e); + SubscriptionUpdate update; + if (UnauthorizedException.class.isInstance(e)) { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.UNAUTHORIZED, + SubscriptionErrorCode.UNAUTHORIZED.getDefaultMsg()); + } else { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + FAILED_TO_FETCH_ATTRIBUTES); + } + sendWsMsg(sessionRef, update); + } + }; + + if (StringUtils.isEmpty(cmd.getScope())) { + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, keys, callback)); + } else { + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), keys, callback)); + } + } + + private void handleWsHistoryCmd(TelemetryWebSocketSessionRef sessionRef, GetHistoryCmd cmd) { + String sessionId = sessionRef.getSessionId(); + WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); + if (sessionMD == null) { + log.warn("[{}] Session meta data not found. ", sessionId); + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + SESSION_META_DATA_NOT_FOUND); + sendWsMsg(sessionRef, update); + return; + } + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty() || cmd.getEntityType() == null || cmd.getEntityType().isEmpty()) { + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, + "Device id is empty!"); + sendWsMsg(sessionRef, update); + return; + } + if (cmd.getKeys() == null || cmd.getKeys().isEmpty()) { + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, + "Keys are empty!"); + sendWsMsg(sessionRef, update); + return; + } + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); + List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); + List queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))) + .collect(Collectors.toList()); + + FutureCallback> callback = new FutureCallback>() { + @Override + public void onSuccess(List data) { + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); + } + + @Override + public void onFailure(Throwable e) { + SubscriptionUpdate update; + if (UnauthorizedException.class.isInstance(e)) { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.UNAUTHORIZED, + SubscriptionErrorCode.UNAUTHORIZED.getDefaultMsg()); + } else { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + FAILED_TO_FETCH_DATA); + } + sendWsMsg(sessionRef, update); + } + }; + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, + on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); + } + + private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef, + AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) { + FutureCallback> callback = new FutureCallback>() { + @Override + public void onSuccess(List data) { + List attributesData = data.stream().map(d -> new BasicTsKvEntry(d.getLastUpdateTs(), d)).collect(Collectors.toList()); + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), attributesData)); + + Map subState = new HashMap<>(attributesData.size()); + attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); + + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.ATTRIBUTES, true, subState, cmd.getScope()); + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); + } + + @Override + public void onFailure(Throwable e) { + log.error(FAILED_TO_FETCH_ATTRIBUTES, e); + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + FAILED_TO_FETCH_ATTRIBUTES); + sendWsMsg(sessionRef, update); + } + }; + + + if (StringUtils.isEmpty(cmd.getScope())) { + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, callback)); + } else { + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), callback)); + } + } + + private void handleWsTimeseriesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { + String sessionId = sessionRef.getSessionId(); + log.debug("[{}] Processing: {}", sessionId, cmd); + + if (validateSessionMetadata(sessionRef, cmd, sessionId)) { + if (cmd.isUnsubscribe()) { + unsubscribe(sessionRef, cmd, sessionId); + } else if (validateSubscriptionCmd(sessionRef, cmd)) { + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); + Optional> keysOptional = getKeys(cmd); + + if (keysOptional.isPresent()) { + handleWsTimeseriesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId); + } else { + handleWsTimeseriesSubscription(sessionRef, cmd, sessionId, entityId); + } + } + } + } + + private void handleWsTimeseriesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, + TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { + long startTs; + if (cmd.getTimeWindow() > 0) { + List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); + log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); + startTs = cmd.getStartTs(); + long endTs = cmd.getStartTs() + cmd.getTimeWindow(); + List queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getInterval(), + getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); + + final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, + on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); + } else { + List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); + startTs = System.currentTimeMillis(); + log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId); + final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, + on(r -> Futures.addCallback(tsService.findLatest(entityId, keys), callback, executor), callback::onFailure)); + } + } + + private void handleWsTimeseriesSubscription(TelemetryWebSocketSessionRef sessionRef, + TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { + FutureCallback> callback = new FutureCallback>() { + @Override + public void onSuccess(List data) { + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); + Map subState = new HashMap<>(data.size()); + data.forEach(v -> subState.put(v.getKey(), v.getTs())); + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.TIMESERIES, true, subState, cmd.getScope()); + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); + } + + @Override + public void onFailure(Throwable e) { + SubscriptionUpdate update; + if (UnauthorizedException.class.isInstance(e)) { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.UNAUTHORIZED, + SubscriptionErrorCode.UNAUTHORIZED.getDefaultMsg()); + } else { + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + FAILED_TO_FETCH_DATA); + } + sendWsMsg(sessionRef, update); + } + }; + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, + on(r -> Futures.addCallback(tsService.findAllLatest(entityId), callback, executor), callback::onFailure)); + } + + private FutureCallback> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List keys) { + return new FutureCallback>() { + @Override + public void onSuccess(List data) { + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); + + Map subState = new HashMap<>(keys.size()); + keys.forEach(key -> subState.put(key, startTs)); + data.forEach(v -> subState.put(v.getKey(), v.getTs())); + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, TelemetryFeature.TIMESERIES, false, subState, cmd.getScope()); + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); + } + + @Override + public void onFailure(Throwable e) { + log.error(FAILED_TO_FETCH_DATA, e); + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + FAILED_TO_FETCH_DATA); + sendWsMsg(sessionRef, update); + } + }; + } + + private void unsubscribe(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { + subscriptionManager.cleanupLocalWsSessionSubscriptions(sessionRef, sessionId); + } else { + subscriptionManager.removeSubscription(sessionId, cmd.getCmdId()); + } + } + + private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, + "Device id is empty!"); + sendWsMsg(sessionRef, update); + return false; + } + return true; + } + + private boolean validateSessionMetadata(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { + WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); + if (sessionMD == null) { + log.warn("[{}] Session meta data not found. ", sessionId); + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, + SESSION_META_DATA_NOT_FOUND); + sendWsMsg(sessionRef, update); + return false; + } else { + return true; + } + } + + private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, SubscriptionUpdate update) { + try { + msgEndpoint.send(sessionRef, jsonMapper.writeValueAsString(update)); + } catch (JsonProcessingException e) { + log.warn("[{}] Failed to encode reply: {}", sessionRef.getSessionId(), update, e); + } catch (IOException e) { + log.warn("[{}] Failed to send reply: {}", sessionRef.getSessionId(), update, e); + } + } + + private static Optional> getKeys(TelemetryPluginCmd cmd) { + if (!StringUtils.isEmpty(cmd.getKeys())) { + Set keys = new HashSet<>(); + Collections.addAll(keys, cmd.getKeys().split(",")); + return Optional.of(keys); + } else { + return Optional.empty(); + } + } + + private ListenableFuture> mergeAllAttributesFutures(List>> futures) { + return Futures.transform(Futures.successfulAsList(futures), + (Function>, ? extends List>) input -> { + List tmp = new ArrayList<>(); + if (input != null) { + input.forEach(tmp::addAll); + } + return tmp; + }, executor); + } + + private FutureCallback getAttributesFetchCallback(final EntityId entityId, final List keys, final FutureCallback> callback) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable ValidationResult result) { + List>> futures = new ArrayList<>(); + for (String scope : DataConstants.allScopes()) { + futures.add(attributesService.find(entityId, scope, keys)); + } + + ListenableFuture> future = mergeAllAttributesFutures(futures); + Futures.addCallback(future, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + + private FutureCallback getAttributesFetchCallback(final EntityId entityId, final String scope, final List keys, final FutureCallback> callback) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable ValidationResult result) { + Futures.addCallback(attributesService.find(entityId, scope, keys), callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + + private FutureCallback getAttributesFetchCallback(final EntityId entityId, final FutureCallback> callback) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable ValidationResult result) { + List>> futures = new ArrayList<>(); + for (String scope : DataConstants.allScopes()) { + futures.add(attributesService.findAll(entityId, scope)); + } + + ListenableFuture> future = mergeAllAttributesFutures(futures); + Futures.addCallback(future, callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + + private FutureCallback getAttributesFetchCallback(final EntityId entityId, final String scope, final FutureCallback> callback) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable ValidationResult result) { + Futures.addCallback(attributesService.findAll(entityId, scope), callback); + } + + @Override + public void onFailure(Throwable t) { + callback.onFailure(t); + } + }; + } + + private FutureCallback on(Consumer success, Consumer failure) { + return new FutureCallback() { + @Override + public void onSuccess(@Nullable ValidationResult result) { + success.accept(result); + } + + @Override + public void onFailure(Throwable t) { + failure.accept(t); + } + }; + } + + + private static Aggregation getAggregation(String agg) { + return StringUtils.isEmpty(agg) ? DEFAULT_AGGREGATION : Aggregation.valueOf(agg); + } + + private int getLimit(int limit) { + return limit == 0 ? DEFAULT_LIMIT : limit; + } +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/SessionEvent.java b/application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java similarity index 96% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/SessionEvent.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java index 928dd91a7c..75d84c94fc 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/SessionEvent.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/SessionEvent.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.ws; +package org.thingsboard.server.service.telemetry; import lombok.Getter; import lombok.ToString; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryFeature.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java similarity index 91% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryFeature.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java index b776e054a9..a8f87d323c 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryFeature.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryFeature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.handlers; +package org.thingsboard.server.service.telemetry; /** * Created by ashvayka on 08.05.17. diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java new file mode 100644 index 0000000000..f1802acde5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2018 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.telemetry; + +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.service.telemetry.sub.SubscriptionState; + +/** + * Created by ashvayka on 27.03.18. + */ +public interface TelemetrySubscriptionService extends RuleEngineTelemetryService { + + void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub); + + void cleanupLocalWsSessionSubscriptions(TelemetryWebSocketSessionRef sessionRef, String sessionId); + + void removeSubscription(String sessionId, int cmdId); + + void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data); + + void onRemoteSubscriptionUpdate(ServerAddress serverAddress, byte[] bytes); + + void onRemoteSubscriptionClose(ServerAddress serverAddress, byte[] bytes); + + void onRemoteSessionClose(ServerAddress serverAddress, byte[] bytes); + + void onRemoteAttributesUpdate(ServerAddress serverAddress, byte[] bytes); + + void onRemoteTsUpdate(ServerAddress serverAddress, byte[] bytes); + + void onClusterUpdate(); +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RpcMsgHandler.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java similarity index 65% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RpcMsgHandler.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java index 8875209618..00fb80a522 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RpcMsgHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.handlers; +package org.thingsboard.server.service.telemetry; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; +import java.io.IOException; /** - * @author Andrew Shvayka + * Created by ashvayka on 27.03.18. */ -public interface RpcMsgHandler { +public interface TelemetryWebSocketMsgEndpoint { - void process(PluginContext ctx, RpcMsg msg); + void send(TelemetryWebSocketSessionRef sessionRef, String msg) throws IOException; + + void close(TelemetryWebSocketSessionRef sessionRef) throws IOException; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java new file mode 100644 index 0000000000..c2fba93b83 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketService.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2018 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.telemetry; + +import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate; + +/** + * Created by ashvayka on 27.03.18. + */ +public interface TelemetryWebSocketService { + + void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent sessionEvent); + + void handleWebSocketMsg(TelemetryWebSocketSessionRef sessionRef, String msg); + + void sendWsMsg(String sessionId, SubscriptionUpdate update); +} diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java new file mode 100644 index 0000000000..53438c533b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketSessionRef.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2018 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.telemetry; + +import lombok.Getter; +import org.thingsboard.server.service.security.model.SecurityUser; + +import java.net.InetSocketAddress; +import java.util.Objects; + +/** + * Created by ashvayka on 27.03.18. + */ +public class TelemetryWebSocketSessionRef { + + private static final long serialVersionUID = 1L; + + @Getter + private final String sessionId; + @Getter + private final SecurityUser securityCtx; + @Getter + private final InetSocketAddress localAddress; + @Getter + private final InetSocketAddress remoteAddress; + + public TelemetryWebSocketSessionRef(String sessionId, SecurityUser securityCtx, InetSocketAddress localAddress, InetSocketAddress remoteAddress) { + this.sessionId = sessionId; + this.securityCtx = securityCtx; + this.localAddress = localAddress; + this.remoteAddress = remoteAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TelemetryWebSocketSessionRef that = (TelemetryWebSocketSessionRef) o; + return Objects.equals(sessionId, that.sessionId); + } + + @Override + public int hashCode() { + return Objects.hash(sessionId); + } + + @Override + public String toString() { + return "TelemetryWebSocketSessionRef{" + + "sessionId='" + sessionId + '\'' + + ", localAddress=" + localAddress + + ", remoteAddress=" + remoteAddress + + '}'; + } +} diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/processor/AlarmDeduplicationProcessorConfiguration.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java similarity index 74% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/processor/AlarmDeduplicationProcessorConfiguration.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java index e63873598d..e2568cffe2 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/processor/AlarmDeduplicationProcessorConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketTextMsg.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.processor; +package org.thingsboard.server.service.telemetry; import lombok.Data; /** - * @author Andrew Shvayka + * Created by ashvayka on 27.03.18. */ @Data -public class AlarmDeduplicationProcessorConfiguration { +public class TelemetryWebSocketTextMsg { - private String alarmIdTemplate; - private String alarmBodyTemplate; + private final TelemetryWebSocketSessionRef sessionRef; + private final String payload; } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/TsData.java b/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java similarity index 94% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/TsData.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java index 2b48567fca..b8333ce46a 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/TsData.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/TsData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry; +package org.thingsboard.server.service.telemetry; public class TsData implements Comparable{ diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/WsSessionMetaData.java b/application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java similarity index 78% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/WsSessionMetaData.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java index 28d108ce55..d63c3d40b5 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/WsSessionMetaData.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/WsSessionMetaData.java @@ -13,24 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.ws; +package org.thingsboard.server.service.telemetry; -public class WsSessionMetaData { - private PluginWebsocketSessionRef sessionRef; +/** + * Created by ashvayka on 27.03.18. + */ +public class WsSessionMetaData { + private TelemetryWebSocketSessionRef sessionRef; private long lastActivityTime; - public WsSessionMetaData(PluginWebsocketSessionRef sessionRef) { + public WsSessionMetaData(TelemetryWebSocketSessionRef sessionRef) { super(); this.sessionRef = sessionRef; this.lastActivityTime = System.currentTimeMillis(); } - public PluginWebsocketSessionRef getSessionRef() { + public TelemetryWebSocketSessionRef getSessionRef() { return sessionRef; } - public void setSessionRef(PluginWebsocketSessionRef sessionRef) { + public void setSessionRef(TelemetryWebSocketSessionRef sessionRef) { this.sessionRef = sessionRef; } @@ -46,5 +49,4 @@ public class WsSessionMetaData { public String toString() { return "WsSessionMetaData [sessionRef=" + sessionRef + ", lastActivityTime=" + lastActivityTime + "]"; } - } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/AttributesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/AttributesSubscriptionCmd.java similarity index 76% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/AttributesSubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/AttributesSubscriptionCmd.java index d116b2e46c..2c61260849 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/AttributesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/AttributesSubscriptionCmd.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; import lombok.NoArgsConstructor; -import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; +import org.thingsboard.server.service.telemetry.TelemetryFeature; /** * @author Andrew Shvayka @@ -25,8 +25,8 @@ import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionT public class AttributesSubscriptionCmd extends SubscriptionCmd { @Override - public SubscriptionType getType() { - return SubscriptionType.ATTRIBUTES; + public TelemetryFeature getType() { + return TelemetryFeature.ATTRIBUTES; } } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/GetHistoryCmd.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/GetHistoryCmd.java similarity index 93% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/GetHistoryCmd.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/GetHistoryCmd.java index 426e434e1a..9fc28f6a3a 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/GetHistoryCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/GetHistoryCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/SubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/SubscriptionCmd.java similarity index 85% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/SubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/SubscriptionCmd.java index b06476ab9d..15701ca2a3 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/SubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/SubscriptionCmd.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; +import org.thingsboard.server.service.telemetry.TelemetryFeature; @NoArgsConstructor @AllArgsConstructor @@ -32,7 +32,7 @@ public abstract class SubscriptionCmd implements TelemetryPluginCmd { private String scope; private boolean unsubscribe; - public abstract SubscriptionType getType(); + public abstract TelemetryFeature getType(); @Override public String toString() { diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmd.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmd.java similarity index 91% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmd.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmd.java index 84ce7953d3..06c6e5a296 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmd.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; /** * @author Andrew Shvayka diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java similarity index 95% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmdsWrapper.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java index c065bd987a..2f5c037bc6 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TelemetryPluginCmdsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TelemetryPluginCmdsWrapper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; import java.util.List; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TimeseriesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TimeseriesSubscriptionCmd.java similarity index 80% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TimeseriesSubscriptionCmd.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TimeseriesSubscriptionCmd.java index 4d64ca70d4..40450e17eb 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/cmd/TimeseriesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/cmd/TimeseriesSubscriptionCmd.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.cmd; +package org.thingsboard.server.service.telemetry.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; +import org.thingsboard.server.service.telemetry.TelemetryFeature; /** * @author Andrew Shvayka @@ -35,7 +35,7 @@ public class TimeseriesSubscriptionCmd extends SubscriptionCmd { private String agg; @Override - public SubscriptionType getType() { - return SubscriptionType.TIMESERIES; + public TelemetryFeature getType() { + return TelemetryFeature.TIMESERIES; } } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/AccessDeniedException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java similarity index 94% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/AccessDeniedException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java index 923ca82fd7..dc30b1ed42 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/AccessDeniedException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/AccessDeniedException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/EntityNotFoundException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java similarity index 94% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/EntityNotFoundException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java index 3fc9f515c3..10ef2b7c3c 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/EntityNotFoundException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/EntityNotFoundException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InternalErrorException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java similarity index 94% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InternalErrorException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java index 25fa28e351..4a3dec61df 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InternalErrorException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InternalErrorException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InvalidParametersException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java similarity index 94% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InvalidParametersException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java index 3c7d227a9c..62b961af58 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/InvalidParametersException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/InvalidParametersException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/ToErrorResponseEntity.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java similarity index 93% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/ToErrorResponseEntity.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java index 0d3345b424..43c747eeb9 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/ToErrorResponseEntity.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/ToErrorResponseEntity.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UnauthorizedException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java similarity index 94% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UnauthorizedException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java index c23b36da7f..d1b6bc0e51 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UnauthorizedException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UnauthorizedException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UncheckedApiException.java b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java similarity index 95% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UncheckedApiException.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java index c594eb97a3..a3df8e5d58 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/exception/UncheckedApiException.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/exception/UncheckedApiException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.exception; +package org.thingsboard.server.service.telemetry.exception; import org.springframework.http.ResponseEntity; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/Subscription.java similarity index 92% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/sub/Subscription.java index fc047130b8..811c055372 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/Subscription.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.sub; +package org.thingsboard.server.service.telemetry.sub; import lombok.AllArgsConstructor; import lombok.Data; -import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.msg.cluster.ServerAddress; +import org.thingsboard.server.service.telemetry.TelemetryFeature; import java.util.Map; @@ -47,7 +47,7 @@ public class Subscription { return getSub().getEntityId(); } - public SubscriptionType getType() { + public TelemetryFeature getType() { return getSub().getType(); } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionErrorCode.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java similarity index 95% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionErrorCode.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java index b43e807da8..9da74d940b 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionErrorCode.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionErrorCode.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.sub; +package org.thingsboard.server.service.telemetry.sub; public enum SubscriptionErrorCode { diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java similarity index 93% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java index c9598efbd9..a088fa9a3b 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionState.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.sub; +package org.thingsboard.server.service.telemetry.sub; import lombok.AllArgsConstructor; import lombok.Getter; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.telemetry.TelemetryFeature; import java.util.Map; @@ -30,7 +31,7 @@ public class SubscriptionState { @Getter private final String wsSessionId; @Getter private final int subscriptionId; @Getter private final EntityId entityId; - @Getter private final SubscriptionType type; + @Getter private final TelemetryFeature type; @Getter private final boolean allKeys; @Getter private final Map keyStates; @Getter private final String scope; diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionUpdate.java similarity index 82% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionUpdate.java rename to application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionUpdate.java index a9a3a3b65f..bbabe7aacb 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/SubscriptionUpdate.java @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.telemetry.sub; +package org.thingsboard.server.service.telemetry.sub; import org.thingsboard.server.common.data.kv.TsKvEntry; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import java.util.stream.Collectors; public class SubscriptionUpdate { @@ -31,12 +35,14 @@ public class SubscriptionUpdate { super(); this.subscriptionId = subscriptionId; this.data = new TreeMap<>(); - for (TsKvEntry tsEntry : data) { - List values = this.data.computeIfAbsent(tsEntry.getKey(), k -> new ArrayList<>()); - Object[] value = new Object[2]; - value[0] = tsEntry.getTs(); - value[1] = tsEntry.getValueAsString(); - values.add(value); + if (data != null) { + for (TsKvEntry tsEntry : data) { + List values = this.data.computeIfAbsent(tsEntry.getKey(), k -> new ArrayList<>()); + Object[] value = new Object[2]; + value[0] = tsEntry.getTs(); + value[1] = tsEntry.getValueAsString(); + values.add(value); + } } } diff --git a/application/src/main/proto/cluster.proto b/application/src/main/proto/cluster.proto index e106d1bf00..ac96010efe 100644 --- a/application/src/main/proto/cluster.proto +++ b/application/src/main/proto/cluster.proto @@ -19,79 +19,112 @@ package cluster; option java_package = "org.thingsboard.server.gen.cluster"; option java_outer_classname = "ClusterAPIProtos"; +service ClusterRpcService { + rpc handleMsgs(stream ClusterMessage) returns (stream ClusterMessage) {} +} +message ClusterMessage { + MessageType messageType = 1; + MessageMataInfo messageMetaInfo = 2; + ServerAddress serverAddress = 3; + bytes payload = 4; +} + message ServerAddress { string host = 1; int32 port = 2; } -message Uid { - sint64 pluginUuidMsb = 1; - sint64 pluginUuidLsb = 2; +message MessageMataInfo { + string payloadMetaInfo = 1; + repeated string tags = 2; } -message PluginAddress { - Uid pluginId = 1; - Uid tenantId = 2; +enum MessageType { + + //Cluster control messages + RPC_SESSION_CREATE_REQUEST_MSG = 0; + TO_ALL_NODES_MSG = 1; + RPC_SESSION_TELL_MSG = 2; + RPC_BROADCAST_MSG = 3; + CONNECT_RPC_MESSAGE =4; + + CLUSTER_ACTOR_MESSAGE = 5; + // Messages related to TelemetrySubscriptionService + CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE = 6; + CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE = 7; + CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE = 8; + CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE = 9; + CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE = 10; + CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE = 11; + CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE = 12; } -message ToPluginRpcMessage { - PluginAddress address = 1; - int32 clazz = 2; - bytes data = 3; +// Messages related to CLUSTER_TELEMETRY_MESSAGE +message SubscriptionProto { + string sessionId = 1; + int32 subscriptionId = 2; + string entityType = 3; + string entityId = 4; + string type = 5; + bool allKeys = 6; + repeated SubscriptionKetStateProto keyStates = 7; + string scope = 8; } -message ToDeviceActorRpcMessage { - bytes data = 1; +message SubscriptionUpdateProto { + string sessionId = 1; + int32 subscriptionId = 2; + int32 errorCode = 3; + string errorMsg = 4; + repeated SubscriptionUpdateValueListProto data = 5; } -message ToDeviceSessionActorRpcMessage { - bytes data = 1; +message AttributeUpdateProto { + string entityType = 1; + string entityId = 2; + string scope = 3; + repeated KeyValueProto data = 4; } -message ToDeviceActorNotificationRpcMessage { - bytes data = 1; +message TimeseriesUpdateProto { + string entityType = 1; + string entityId = 2; + repeated KeyValueProto data = 4; } -message ToAllNodesRpcMessage { - bytes data = 1; +message SessionCloseProto { + string sessionId = 1; } -message ConnectRpcMessage { - ServerAddress serverAddress = 1; +message SubscriptionCloseProto { + string sessionId = 1; + int32 subscriptionId = 2; } -message ToDeviceRpcRequestRpcMessage { - PluginAddress address = 1; - Uid deviceTenantId = 2; - Uid deviceId = 3; - - Uid msgId = 4; - bool oneway = 5; - int64 expTime = 6; - string method = 7; - string params = 8; +message SubscriptionKetStateProto { + string key = 1; + int64 ts = 2; } -message ToPluginRpcResponseRpcMessage { - PluginAddress address = 1; - - Uid msgId = 2; - string response = 3; - string error = 4; +message SubscriptionUpdateValueListProto { + string key = 1; + repeated int64 ts = 2; + repeated string value = 3; } -message ToRpcServerMessage { - ConnectRpcMessage connectMsg = 1; - ToPluginRpcMessage toPluginRpcMsg = 2; - ToDeviceActorRpcMessage toDeviceActorRpcMsg = 3; - ToDeviceSessionActorRpcMessage toDeviceSessionActorRpcMsg = 4; - ToDeviceActorNotificationRpcMessage toDeviceActorNotificationRpcMsg = 5; - ToAllNodesRpcMessage toAllNodesRpcMsg = 6; - ToDeviceRpcRequestRpcMessage toDeviceRpcRequestRpcMsg = 7; - ToPluginRpcResponseRpcMessage toPluginRpcResponseRpcMsg = 8; +message KeyValueProto { + string key = 1; + int64 ts = 2; + int32 valueType = 3; + string strValue = 4; + int64 longValue = 5; + double doubleValue = 6; + bool boolValue = 7; } -service ClusterRpcService { - rpc handlePluginMsgs(stream ToRpcServerMessage) returns (stream ToRpcServerMessage) {} +message FromDeviceRPCResponseProto { + int64 requestIdMSB = 1; + int64 requestIdLSB = 2; + string response = 3; + int32 error = 4; } - diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index 1779912366..978a570e66 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -25,7 +25,7 @@ - + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6a45f42808..5653193d27 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -58,11 +58,13 @@ cluster: hash_function_name: "${CLUSTER_HASH_FUNCTION_NAME:murmur3_128}" # Amount of virtual nodes in consistent hash ring. vitrual_nodes_size: "${CLUSTER_VIRTUAL_NODES_SIZE:16}" + # Queue partition id for current node + partition_id: "${QUEUE_PARTITION_ID:0}" # Plugins configuration parameters plugins: # Comma seperated package list used during classpath scanning for plugins - scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions}" + scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions,org.thingsboard.rule.engine}" # JWT Token parameters security.jwt: @@ -106,7 +108,7 @@ mqtt: # CoAP server parameters coap: # Enable/disable coap transport protocol. - enabled: "${COAP_ENABLED:true}" + enabled: "${COAP_ENABLED:false}" bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" bind_port: "${COAP_BIND_PORT:5683}" adaptor: "${COAP_ADAPTOR_NAME:JsonCoapAdaptor}" @@ -129,9 +131,28 @@ quota: whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}" # Array of blacklist hosts blacklist: "${QUOTA_HOST_BLACKLIST:}" - log: - topSize: 10 - intervalMin: 2 + log: + topSize: 10 + intervalMin: 2 + rule: + tenant: + # Max allowed number of API requests in interval for single tenant + limit: "${QUOTA_TENANT_LIMIT:100000}" + # Interval duration + intervalMs: "${QUOTA_TENANT_INTERVAL_MS:60000}" + # Maximum silence duration for tenant after which Tenant removed from QuotaService. Must be bigger than intervalMs + ttlMs: "${QUOTA_TENANT_TTL_MS:60000}" + # Interval for scheduled task that cleans expired records. TTL is used for expiring + cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}" + # Enable Host API Limits + enabled: "${QUOTA_TENANT_ENABLED:false}" + # Array of whitelist tenants + whitelist: "${QUOTA_TENANT_WHITELIST:}" + # Array of blacklist tenants + blacklist: "${QUOTA_HOST_BLACKLIST:}" + log: + topSize: 10 + intervalMin: 2 database: type: "${DATABASE_TYPE:sql}" # cassandra OR sql @@ -182,11 +203,18 @@ cassandra: default_fetch_size: "${CASSANDRA_DEFAULT_FETCH_SIZE:2000}" # Specify partitioning size for timestamp key-value storage. Example MINUTES, HOURS, DAYS, MONTHS ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}" + ts_key_value_ttl: "${TS_KV_TTL:0}" buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:30000}" + queue: + msg.ttl: 604800 # 7 days + ack.ttl: 604800 # 7 days + partitions.ttl: 604800 # 7 days + partitioning: "HOURS" + # SQL configuration parameters sql: # Specify executor service type used to perform timeseries insert tasks: SINGLE FIXED CACHED @@ -199,25 +227,54 @@ actors: tenant: create_components_on_init: true session: + max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}" sync: # Default timeout for processing request using synchronous session (HTTP, CoAP) in milliseconds timeout: "${ACTORS_SESSION_SYNC_TIMEOUT:10000}" - plugin: - # Default timeout for termination of the plugin actor after it is stopped - termination.delay: "${ACTORS_PLUGIN_TERMINATION_DELAY:60000}" - # Default timeout for processing of particular message by particular plugin - processing.timeout: "${ACTORS_PLUGIN_TIMEOUT:60000}" - # Errors for particular actor are persisted once per specified amount of milliseconds - error_persist_frequency: "${ACTORS_PLUGIN_ERROR_FREQUENCY:3000}" rule: - # Default timeout for termination of the rule actor after it is stopped - termination.delay: "${ACTORS_RULE_TERMINATION_DELAY:30000}" - # Errors for particular actor are persisted once per specified amount of milliseconds - error_persist_frequency: "${ACTORS_RULE_ERROR_FREQUENCY:3000}" + # Specify thread pool size for database request callbacks executor service + db_callback_thread_pool_size: "${ACTORS_RULE_DB_CALLBACK_THREAD_POOL_SIZE:1}" + # Specify thread pool size for javascript executor service + js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:10}" + # Specify thread pool size for mail sender executor service + mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:10}" + # Whether to allow usage of system mail service for rules + allow_system_mail_service: "${ACTORS_RULE_ALLOW_SYSTEM_MAIL_SERVICE:true}" + # Specify thread pool size for external call service + external_call_thread_pool_size: "${ACTORS_RULE_EXTERNAL_CALL_THREAD_POOL_SIZE:10}" + js_sandbox: + # Use Sandboxed (secured) JavaScript environment + use_js_sandbox: "${ACTORS_RULE_JS_SANDBOX_USE_JS_SANDBOX:true}" + # Specify thread pool size for JavaScript sandbox resource monitor + monitor_thread_pool_size: "${ACTORS_RULE_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" + # Maximum CPU time in milliseconds allowed for script execution + max_cpu_time: "${ACTORS_RULE_JS_SANDBOX_MAX_CPU_TIME:100}" + # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted + max_errors: "${ACTORS_RULE_JS_SANDBOX_MAX_ERRORS:3}" + chain: + # Errors for particular actor are persisted once per specified amount of milliseconds + error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" + node: + # Errors for particular actor are persisted once per specified amount of milliseconds + error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" + queue: + # Message queue type + type: "${ACTORS_RULE_QUEUE_TYPE:memory}" + # Message queue maximum size (per tenant) + max_size: "${ACTORS_RULE_QUEUE_MAX_SIZE:100}" + # Message queue cleanup period in seconds + cleanup_period: "${ACTORS_RULE_QUEUE_CLEANUP_PERIOD:3600}" statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" + queue: + # Enable/disable persistence of un-processed messages to the queue + enabled: "${ACTORS_QUEUE_ENABLED:true}" + # Maximum allowed timeout for persistence into the queue + timeout: "${ACTORS_QUEUE_PERSISTENCE_TIMEOUT:30000}" + client_side_rpc: + timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}" cache: # caffeine or redis @@ -283,17 +340,17 @@ spring: database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.HSQLDialect}" datasource: driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.hsqldb.jdbc.JDBCDriver}" - url: "${SPRING_DATASOURCE_URL:jdbc:hsqldb:file:${SQL_DATA_FOLDER:/tmp}/thingsboardDb;sql.enforce_size=false}" + url: "${SPRING_DATASOURCE_URL:jdbc:hsqldb:file:${SQL_DATA_FOLDER:/tmp}/thingsboardDb;sql.enforce_size=false;hsqldb.log_size=5}" username: "${SPRING_DATASOURCE_USERNAME:sa}" password: "${SPRING_DATASOURCE_PASSWORD:}" # PostgreSQL DAO Configuration #spring: # data: -# jpa: +# sql: # repositories: # enabled: "true" -# jpa: +# sql: # hibernate: # ddl-auto: "validate" # database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}" @@ -320,8 +377,7 @@ audit_log: "dashboard": "${AUDIT_LOG_MASK_DASHBOARD:W}" "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}" "user": "${AUDIT_LOG_MASK_USER:W}" - "rule": "${AUDIT_LOG_MASK_RULE:W}" - "plugin": "${AUDIT_LOG_MASK_PLUGIN:W}" + "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" @@ -337,4 +393,11 @@ audit_log: host: "${AUDIT_LOG_SINK_HOST:localhost}" port: "${AUDIT_LOG_SINK_POST:9200}" user_name: "${AUDIT_LOG_SINK_USER_NAME:}" - password: "${AUDIT_LOG_SINK_PASSWORD:}" \ No newline at end of file + password: "${AUDIT_LOG_SINK_PASSWORD:}" + +state: + defaultInactivityTimeoutInSec: 10 + defaultStateCheckIntervalInSec: 10 +# TODO in v2.1 +# defaultStatePersistenceIntervalInSec: 60 +# defaultStatePersistencePack: 100 \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java b/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java deleted file mode 100644 index d77341adee..0000000000 --- a/application/src/test/java/org/thingsboard/server/actors/DefaultActorServiceTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.*; - -import com.google.common.util.concurrent.Futures; -import org.thingsboard.server.actors.service.DefaultActorService; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.plugin.ComponentDescriptor; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.msg.session.*; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.gen.discovery.ServerInstanceProtos; -import org.thingsboard.server.service.cluster.discovery.DiscoveryService; -import org.thingsboard.server.service.cluster.discovery.ServerInstance; -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; -import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; -import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.common.transport.auth.DeviceAuthResult; -import org.thingsboard.server.common.transport.auth.DeviceAuthService; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; -import org.thingsboard.server.common.data.security.DeviceTokenCredentials; -import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.rule.RuleService; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.test.util.ReflectionTestUtils; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class DefaultActorServiceTest { - - private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); - - private static final String PLUGIN_ID = "9fb2e951-e298-4acb-913a-db69af8a15f4"; - private static final String FILTERS_CONFIGURATION = - "[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", \"name\":\"TelemetryFilter\", \"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]"; - private static final String ACTION_CONFIGURATION = "{\"pluginToken\":\"telemetry\", \"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", \"configuration\":{}}"; - private static final String PLUGIN_CONFIGURATION = "{}"; - private DefaultActorService actorService; - private ActorSystemContext actorContext; - - private PluginService pluginService; - private RuleService ruleService; - private DeviceAuthService deviceAuthService; - private DeviceService deviceService; - private TimeseriesService tsService; - private TenantService tenantService; - private ClusterRpcService rpcService; - private DiscoveryService discoveryService; - private ClusterRoutingService routingService; - private AttributesService attributesService; - private ComponentDiscoveryService componentService; - private EventService eventService; - private ServerInstance serverInstance; - - private RuleMetaData ruleMock; - private PluginMetaData pluginMock; - private RuleId ruleId = new RuleId(UUID.randomUUID()); - private PluginId pluginId = new PluginId(UUID.fromString(PLUGIN_ID)); - private TenantId tenantId = new TenantId(UUID.randomUUID()); - - - @Before - public void before() throws Exception { - actorService = new DefaultActorService(); - actorContext = new ActorSystemContext(); - - tenantService = mock(TenantService.class); - pluginService = mock(PluginService.class); - ruleService = mock(RuleService.class); - deviceAuthService = mock(DeviceAuthService.class); - deviceService = mock(DeviceService.class); - tsService = mock(TimeseriesService.class); - rpcService = mock(ClusterRpcService.class); - discoveryService = mock(DiscoveryService.class); - routingService = mock(ClusterRoutingService.class); - attributesService = mock(AttributesService.class); - componentService = mock(ComponentDiscoveryService.class); - eventService = mock(EventService.class); - serverInstance = new ServerInstance(ServerInstanceProtos.ServerInfo.newBuilder().setHost("localhost").setPort(8080).build()); - - ReflectionTestUtils.setField(actorService, "actorContext", actorContext); - ReflectionTestUtils.setField(actorService, "rpcService", rpcService); - ReflectionTestUtils.setField(actorService, "discoveryService", discoveryService); - - ReflectionTestUtils.setField(actorContext, "syncSessionTimeout", 10000L); - ReflectionTestUtils.setField(actorContext, "pluginActorTerminationDelay", 10000L); - ReflectionTestUtils.setField(actorContext, "pluginErrorPersistFrequency", 10000L); - ReflectionTestUtils.setField(actorContext, "ruleActorTerminationDelay", 10000L); - ReflectionTestUtils.setField(actorContext, "ruleErrorPersistFrequency", 10000L); - ReflectionTestUtils.setField(actorContext, "pluginProcessingTimeout", 60000L); - ReflectionTestUtils.setField(actorContext, "tenantService", tenantService); - ReflectionTestUtils.setField(actorContext, "pluginService", pluginService); - ReflectionTestUtils.setField(actorContext, "ruleService", ruleService); - ReflectionTestUtils.setField(actorContext, "deviceAuthService", deviceAuthService); - ReflectionTestUtils.setField(actorContext, "deviceService", deviceService); - ReflectionTestUtils.setField(actorContext, "tsService", tsService); - ReflectionTestUtils.setField(actorContext, "rpcService", rpcService); - ReflectionTestUtils.setField(actorContext, "discoveryService", discoveryService); - ReflectionTestUtils.setField(actorContext, "tsService", tsService); - ReflectionTestUtils.setField(actorContext, "routingService", routingService); - ReflectionTestUtils.setField(actorContext, "attributesService", attributesService); - ReflectionTestUtils.setField(actorContext, "componentService", componentService); - ReflectionTestUtils.setField(actorContext, "eventService", eventService); - - - when(routingService.resolveById((EntityId) any())).thenReturn(Optional.empty()); - - when(discoveryService.getCurrentServer()).thenReturn(serverInstance); - - ruleMock = mock(RuleMetaData.class); - when(ruleMock.getId()).thenReturn(ruleId); - when(ruleMock.getState()).thenReturn(ComponentLifecycleState.ACTIVE); - when(ruleMock.getPluginToken()).thenReturn("telemetry"); - TextPageData systemRules = new TextPageData<>(Collections.emptyList(), null, false); - TextPageData tenantRules = new TextPageData<>(Collections.singletonList(ruleMock), null, false); - when(ruleService.findSystemRules(any())).thenReturn(systemRules); - when(ruleService.findTenantRules(any(), any())).thenReturn(tenantRules); - when(ruleService.findRuleById(ruleId)).thenReturn(ruleMock); - - pluginMock = mock(PluginMetaData.class); - when(pluginMock.getTenantId()).thenReturn(SYSTEM_TENANT); - when(pluginMock.getId()).thenReturn(pluginId); - when(pluginMock.getState()).thenReturn(ComponentLifecycleState.ACTIVE); - TextPageData systemPlugins = new TextPageData<>(Collections.singletonList(pluginMock), null, false); - TextPageData tenantPlugins = new TextPageData<>(Collections.emptyList(), null, false); - when(pluginService.findSystemPlugins(any())).thenReturn(systemPlugins); - when(pluginService.findTenantPlugins(any(), any())).thenReturn(tenantPlugins); - when(pluginService.findPluginByApiToken("telemetry")).thenReturn(pluginMock); - when(pluginService.findPluginById(pluginId)).thenReturn(pluginMock); - - TextPageData tenants = new TextPageData<>(Collections.emptyList(), null, false); - when(tenantService.findTenants(any())).thenReturn(tenants); - } - - private void initActorSystem() { - actorService.initActorSystem(); - } - - @After - public void after() { - actorService.stopActorSystem(); - } - - @Test - public void testBasicPostWithSyncSession() throws Exception { - SessionContext ssnCtx = mock(SessionContext.class); - KvEntry entry1 = new StringDataEntry("key1", "value1"); - KvEntry entry2 = new StringDataEntry("key2", "value2"); - BasicTelemetryUploadRequest telemetry = new BasicTelemetryUploadRequest(); - long ts = 42; - telemetry.add(ts, entry1); - telemetry.add(ts, entry2); - BasicAdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ssnCtx, telemetry); - - DeviceId deviceId = new DeviceId(UUID.randomUUID()); - - DeviceCredentialsFilter filter = new DeviceTokenCredentials("token1"); - Device device = mock(Device.class); - - when(device.getId()).thenReturn(deviceId); - when(device.getTenantId()).thenReturn(tenantId); - when(ssnCtx.getSessionId()).thenReturn(new DummySessionID("session1")); - when(ssnCtx.getSessionType()).thenReturn(SessionType.SYNC); - when(deviceAuthService.process(filter)).thenReturn(DeviceAuthResult.of(deviceId)); - when(deviceService.findDeviceById(deviceId)).thenReturn(device); - - ObjectMapper ruleMapper = new ObjectMapper(); - when(ruleMock.getFilters()).thenReturn(ruleMapper.readTree(FILTERS_CONFIGURATION)); - when(ruleMock.getAction()).thenReturn(ruleMapper.readTree(ACTION_CONFIGURATION)); - - ComponentDescriptor filterComp = new ComponentDescriptor(); - filterComp.setClazz("org.thingsboard.server.extensions.core.filter.MsgTypeFilter"); - filterComp.setType(ComponentType.FILTER); - when(componentService.getComponent("org.thingsboard.server.extensions.core.filter.MsgTypeFilter")) - .thenReturn(Optional.of(filterComp)); - - ComponentDescriptor actionComp = new ComponentDescriptor(); - actionComp.setClazz("org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction"); - actionComp.setType(ComponentType.ACTION); - when(componentService.getComponent("org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction")) - .thenReturn(Optional.of(actionComp)); - - ObjectMapper pluginMapper = new ObjectMapper(); - JsonNode pluginAdditionalInfo = pluginMapper.readTree(PLUGIN_CONFIGURATION); - when(pluginMock.getConfiguration()).thenReturn(pluginAdditionalInfo); - when(pluginMock.getClazz()).thenReturn(TelemetryStoragePlugin.class.getName()); - - when(attributesService.findAll(deviceId, DataConstants.CLIENT_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); - when(attributesService.findAll(deviceId, DataConstants.SHARED_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); - when(attributesService.findAll(deviceId, DataConstants.SERVER_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); - - initActorSystem(); - Thread.sleep(1000); - actorService.process(new BasicToDeviceActorSessionMsg(device, msg)); - - // Check that device data was saved to DB; - List expected = new ArrayList<>(); - expected.add(new BasicTsKvEntry(ts, entry1)); - expected.add(new BasicTsKvEntry(ts, entry2)); - verify(tsService, Mockito.timeout(5000)).save(deviceId, expected, 0L); - } - -} diff --git a/application/src/test/java/org/thingsboard/server/actors/DummySessionID.java b/application/src/test/java/org/thingsboard/server/actors/DummySessionID.java deleted file mode 100644 index 9324e26c3b..0000000000 --- a/application/src/test/java/org/thingsboard/server/actors/DummySessionID.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2018 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.actors; - -import org.thingsboard.server.common.data.id.SessionId; - -public class DummySessionID implements SessionId { - - @Override - public String toString() { - return id; - } - - private final String id; - - public DummySessionID(String id) { - this.id = id; - } - - @Override - public String toUidStr() { - return id; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DummySessionID other = (DummySessionID) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - return true; - } - -} diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index b92e464a6e..3ec4dc815b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -96,6 +96,8 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC @Slf4j public abstract class AbstractControllerTest { + protected ObjectMapper mapper = new ObjectMapper(); + protected static final String TEST_TENANT_NAME = "TEST TENANT"; protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java new file mode 100644 index 0000000000..1b042a821c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2018 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.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.dao.queue.MsgQueue; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.service.queue.MsgQueueService; + +import java.io.IOException; +import java.util.function.Predicate; + +/** + * Created by ashvayka on 20.03.18. + */ +public class AbstractRuleEngineControllerTest extends AbstractControllerTest { + + @Autowired + protected RuleChainService ruleChainService; + + @Autowired + protected MsgQueueService msgQueueService; + + protected RuleChain saveRuleChain(RuleChain ruleChain) throws Exception { + return doPost("/api/ruleChain", ruleChain, RuleChain.class); + } + + protected RuleChain getRuleChain(RuleChainId ruleChainId) throws Exception { + return doGet("/api/ruleChain/" + ruleChainId.getId().toString(), RuleChain.class); + } + + protected RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMD) throws Exception { + return doPost("/api/ruleChain/metadata", ruleChainMD, RuleChainMetaData.class); + } + + protected RuleChainMetaData getRuleChainMetaData(RuleChainId ruleChainId) throws Exception { + return doGet("/api/ruleChain/metadata/" + ruleChainId.getId().toString(), RuleChainMetaData.class); + } + + protected TimePageData getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception { + TimePageLink pageLink = new TimePageLink(limit); + return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&", + new TypeReference>() { + }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG_RULE_NODE, tenantId.getId()); + } + + protected JsonNode getMetadata(Event outEvent) { + String metaDataStr = outEvent.getBody().get("metadata").asText(); + try { + return mapper.readTree(metaDataStr); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected Predicate filterByCustomEvent() { + return event -> event.getBody().get("msgType").textValue().equals("CUSTOM"); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java index 4346538ad7..62f7562c3a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java @@ -20,14 +20,13 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.thingsboard.rule.engine.filter.TbJsFilterNode; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction; -import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; import java.util.List; @@ -35,7 +34,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. public abstract class BaseComponentDescriptorControllerTest extends AbstractControllerTest { - private static final int AMOUNT_OF_DEFAULT_PLUGINS_DESCRIPTORS = 5; + private static final int AMOUNT_OF_DEFAULT_FILTER_NODES = 4; private Tenant savedTenant; private User tenantAdmin; @@ -69,38 +68,28 @@ public abstract class BaseComponentDescriptorControllerTest extends AbstractCont @Test public void testGetByClazz() throws Exception { ComponentDescriptor descriptor = - doGet("/api/component/" + TelemetryStoragePlugin.class.getName(), ComponentDescriptor.class); + doGet("/api/component/" + TbJsFilterNode.class.getName(), ComponentDescriptor.class); Assert.assertNotNull(descriptor); Assert.assertNotNull(descriptor.getId()); Assert.assertNotNull(descriptor.getName()); Assert.assertEquals(ComponentScope.TENANT, descriptor.getScope()); - Assert.assertEquals(ComponentType.PLUGIN, descriptor.getType()); + Assert.assertEquals(ComponentType.FILTER, descriptor.getType()); Assert.assertEquals(descriptor.getClazz(), descriptor.getClazz()); } @Test public void testGetByType() throws Exception { List descriptors = readResponse( - doGet("/api/components/" + ComponentType.PLUGIN).andExpect(status().isOk()), new TypeReference>() { + doGet("/api/components/" + ComponentType.FILTER).andExpect(status().isOk()), new TypeReference>() { }); Assert.assertNotNull(descriptors); - Assert.assertEquals(AMOUNT_OF_DEFAULT_PLUGINS_DESCRIPTORS, descriptors.size()); + Assert.assertTrue(descriptors.size() >= AMOUNT_OF_DEFAULT_FILTER_NODES); for (ComponentType type : ComponentType.values()) { doGet("/api/components/" + type).andExpect(status().isOk()); } } - @Test - public void testGetActionsByType() throws Exception { - List descriptors = readResponse( - doGet("/api/components/actions/" + TelemetryStoragePlugin.class.getName()).andExpect(status().isOk()), new TypeReference>() { - }); - - Assert.assertNotNull(descriptors); - Assert.assertEquals(1, descriptors.size()); - Assert.assertEquals(TelemetryPluginAction.class.getName(), descriptors.get(0).getClazz()); - } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BasePluginControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BasePluginControllerTest.java deleted file mode 100644 index 915015fcd6..0000000000 --- a/application/src/test/java/org/thingsboard/server/controller/BasePluginControllerTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public abstract class BasePluginControllerTest extends AbstractControllerTest { - - private IdComparator idComparator = new IdComparator<>(); - - private final ObjectMapper mapper = new ObjectMapper(); - private Tenant savedTenant; - private User tenantAdmin; - - @Before - public void beforeTest() throws Exception { - loginSysAdmin(); - - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - Assert.assertNotNull(savedTenant); - - tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - } - - @After - public void afterTest() throws Exception { - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); - } - - @Test - public void testSavePlugin() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); - plugin.setName("My plugin"); - doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); - plugin.setApiToken("myplugin"); - doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); - plugin.setConfiguration(mapper.readTree("{}")); - doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - - Assert.assertNotNull(savedPlugin); - Assert.assertNotNull(savedPlugin.getId()); - Assert.assertTrue(savedPlugin.getCreatedTime() > 0); - Assert.assertEquals(savedTenant.getId(), savedPlugin.getTenantId()); - } - - @Test - public void testFindPluginById() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin"); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - PluginMetaData foundPlugin = doGet("/api/plugin/" + savedPlugin.getId().getId().toString(), PluginMetaData.class); - Assert.assertNotNull(foundPlugin); - Assert.assertEquals(savedPlugin, foundPlugin); - } - - @Test - public void testActivatePlugin() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin"); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - - doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); - } - - @Test - public void testSuspendPlugin() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin"); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - - doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); - - RuleMetaData rule = BaseRuleControllerTest.createRuleMetaData(savedPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); - - doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/suspend").andExpect(status().isBadRequest()); - - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/suspend").andExpect(status().isOk()); - - doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/suspend").andExpect(status().isOk()); - } - - @Test - public void testDeletePluginById() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin"); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - - RuleMetaData rule = BaseRuleControllerTest.createRuleMetaData(savedPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - - doDelete("/api/plugin/" + savedPlugin.getId().getId()).andExpect(status().isBadRequest()); - - doDelete("/api/rule/" + savedRule.getId().getId()).andExpect(status().isOk()); - - doDelete("/api/plugin/" + savedPlugin.getId().getId()).andExpect(status().isOk()); - doGet("/api/plugin/" + savedPlugin.getId().getId().toString()).andExpect(status().isNotFound()); - } - - @Test - public void testFindPluginByToken() throws Exception { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin"); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - - PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); - PluginMetaData foundPlugin = doGet("/api/plugin/token/" + "myplugin", PluginMetaData.class); - Assert.assertNotNull(foundPlugin); - Assert.assertEquals(savedPlugin, foundPlugin); - } - - @Test - public void testFindCurrentTenantPlugins() throws Exception { - List plugins = testPluginsCreation("/api/plugin"); - for (PluginMetaData plugin : plugins) { - doDelete("/api/plugin/" + plugin.getId().getId()).andExpect(status().isOk()); - } - } - - @Test - public void testFindSystemPlugins() throws Exception { - loginSysAdmin(); - List plugins = testPluginsCreation("/api/plugin/system"); - for (PluginMetaData plugin : plugins) { - doDelete("/api/plugin/" + plugin.getId().getId()).andExpect(status().isOk()); - } - } - - private List testPluginsCreation(String url) throws Exception { - List plugins = new ArrayList<>(); - for (int i = 0; i < 111; i++) { - PluginMetaData plugin = new PluginMetaData(); - plugin.setName("My plugin"); - plugin.setApiToken("myplugin" + i); - plugin.setConfiguration(mapper.readTree("{}")); - plugin.setClazz(TelemetryStoragePlugin.class.getName()); - plugins.add(doPost("/api/plugin", plugin, PluginMetaData.class)); - } - - List loadedPlugins = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(23); - TextPageData pageData; - do { - pageData = doGetTypedWithPageLink(url + "?", - new TypeReference>() { - }, pageLink); - loadedPlugins.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - loadedPlugins = loadedPlugins.stream() - .filter(p -> !p.getName().equals("System Telemetry Plugin")) - .filter(p -> !p.getName().equals("Mail Sender Plugin")) - .filter(p -> !p.getName().equals("System RPC Plugin")) - .collect(Collectors.toList()); - - Collections.sort(plugins, idComparator); - Collections.sort(loadedPlugins, idComparator); - - Assert.assertEquals(plugins, loadedPlugins); - return loadedPlugins; - } -} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseRuleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseRuleControllerTest.java deleted file mode 100644 index 9236dfec06..0000000000 --- a/application/src/test/java/org/thingsboard/server/controller/BaseRuleControllerTest.java +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright © 2016-2018 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.controller; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public abstract class BaseRuleControllerTest extends AbstractControllerTest { - - private IdComparator idComparator = new IdComparator<>(); - - private static final ObjectMapper mapper = new ObjectMapper(); - private Tenant savedTenant; - private User tenantAdmin; - private PluginMetaData sysPlugin; - private PluginMetaData tenantPlugin; - - @Before - public void beforeTest() throws Exception { - loginSysAdmin(); - - sysPlugin = new PluginMetaData(); - sysPlugin.setName("Sys plugin"); - sysPlugin.setApiToken("sysplugin"); - sysPlugin.setConfiguration(mapper.readTree("{}")); - sysPlugin.setClazz(TelemetryStoragePlugin.class.getName()); - sysPlugin = doPost("/api/plugin", sysPlugin, PluginMetaData.class); - - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - Assert.assertNotNull(savedTenant); - - tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - - tenantPlugin = new PluginMetaData(); - tenantPlugin.setName("My plugin"); - tenantPlugin.setApiToken("myplugin"); - tenantPlugin.setConfiguration(mapper.readTree("{}")); - tenantPlugin.setClazz(TelemetryStoragePlugin.class.getName()); - tenantPlugin = doPost("/api/plugin", tenantPlugin, PluginMetaData.class); - } - - @After - public void afterTest() throws Exception { - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); - - doDelete("/api/plugin/" + sysPlugin.getId().getId()).andExpect(status().isOk()); - } - - @Test - public void testSaveRule() throws Exception { - RuleMetaData rule = new RuleMetaData(); - doPost("/api/rule", rule).andExpect(status().isBadRequest()); - rule.setName("My Rule"); - doPost("/api/rule", rule).andExpect(status().isBadRequest()); - rule.setPluginToken(tenantPlugin.getApiToken()); - doPost("/api/rule", rule).andExpect(status().isBadRequest()); - rule.setFilters(mapper.readTree("[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", " + - "\"name\":\"TelemetryFilter\", " + - "\"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]")); - doPost("/api/rule", rule).andExpect(status().isBadRequest()); - rule.setAction(mapper.readTree("{\"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", \"configuration\":{\"timeUnit\":\"DAYS\", \"ttlValue\":1}}")); - - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - Assert.assertNotNull(savedRule); - Assert.assertNotNull(savedRule.getId()); - Assert.assertTrue(savedRule.getCreatedTime() > 0); - Assert.assertEquals(savedTenant.getId(), savedRule.getTenantId()); - } - - @Test - public void testFindRuleById() throws Exception { - RuleMetaData rule = createRuleMetaData(tenantPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - - RuleMetaData foundRule = doGet("/api/rule/" + savedRule.getId().getId().toString(), RuleMetaData.class); - Assert.assertNotNull(foundRule); - Assert.assertEquals(savedRule, foundRule); - } - - @Test - public void testFindRuleByPluginToken() throws Exception { - RuleMetaData rule = createRuleMetaData(tenantPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - - List foundRules = doGetTyped("/api/rule/token/" + savedRule.getPluginToken(), - new TypeReference>() { - }); - Assert.assertNotNull(foundRules); - Assert.assertEquals(1, foundRules.size()); - Assert.assertEquals(savedRule, foundRules.get(0)); - } - - @Test - public void testActivateRule() throws Exception { - RuleMetaData rule = createRuleMetaData(tenantPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isBadRequest()); - - doPost("/api/plugin/" + tenantPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); - - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); - } - - @Test - public void testSuspendRule() throws Exception { - RuleMetaData rule = createRuleMetaData(tenantPlugin); - RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); - - doPost("/api/plugin/" + tenantPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); - doPost("/api/rule/" + savedRule.getId().getId().toString() + "/suspend").andExpect(status().isOk()); - } - - @Test - public void testFindSystemRules() throws Exception { - loginSysAdmin(); - List rules = testRulesCreation("/api/rule/system", sysPlugin); - for (RuleMetaData rule : rules) { - doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); - } - loginTenantAdmin(); - } - - @Test - public void testFindCurrentTenantPlugins() throws Exception { - List rules = testRulesCreation("/api/rule", tenantPlugin); - for (RuleMetaData rule : rules) { - doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); - } - } - - @Test - public void testFindTenantPlugins() throws Exception { - List rules = testRulesCreation("/api/rule", tenantPlugin); - loginSysAdmin(); - List loadedRules = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(3); - TextPageData pageData; - do { - pageData = doGetTypedWithPageLink("/api/rule/tenant/" + savedTenant.getId().getId().toString() + "?", - new TypeReference>() { - }, pageLink); - loadedRules.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - Collections.sort(rules, idComparator); - Collections.sort(loadedRules, idComparator); - - Assert.assertEquals(rules, loadedRules); - - for (RuleMetaData rule : rules) { - doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); - } - } - - private List testRulesCreation(String url, PluginMetaData plugin) throws Exception { - List rules = new ArrayList<>(); - for (int i = 0; i < 6; i++) { - RuleMetaData rule = createRuleMetaData(plugin); - rule.setPluginToken(plugin.getApiToken()); - rule.setName(rule.getName() + i); - rules.add(doPost("/api/rule", rule, RuleMetaData.class)); - } - - List loadedRules = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(3); - TextPageData pageData; - do { - pageData = doGetTypedWithPageLink(url + "?", - new TypeReference>() { - }, pageLink); - loadedRules.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - loadedRules = loadedRules.stream().filter(p -> !p.getName().equals("System Telemetry Rule")).collect(Collectors.toList()); - - Collections.sort(rules, idComparator); - Collections.sort(loadedRules, idComparator); - - Assert.assertEquals(rules, loadedRules); - return loadedRules; - } - - public static RuleMetaData createRuleMetaData(PluginMetaData plugin) throws IOException { - RuleMetaData rule = new RuleMetaData(); - rule.setName("My Rule"); - rule.setPluginToken(plugin.getApiToken()); - rule.setFilters(mapper.readTree("[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", " + - "\"name\":\"TelemetryFilter\", " + - "\"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]")); - rule.setAction(mapper.readTree("{\"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", " + - "\"configuration\":{\"timeUnit\":\"DAYS\", \"ttlValue\":1}}")); - return rule; - } -} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/RuleControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java similarity index 69% rename from application/src/test/java/org/thingsboard/server/controller/sql/RuleControllerSqlTest.java rename to application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java index cf651dac02..4b71cae015 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/RuleControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java @@ -13,14 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.controller.sql; +package org.thingsboard.server.mqtt; -import org.thingsboard.server.controller.BaseRuleControllerTest; -import org.thingsboard.server.dao.service.DaoSqlTest; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; /** - * Created by Valerii Sosliuk on 6/28/2017. + * Created by ashvayka on 11.05.18. */ -@DaoSqlTest -public class RuleControllerSqlTest extends BaseRuleControllerTest { +public class DbConfigurationTestRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + return null; + } } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index b732e66f48..f3e29dc1ad 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -15,23 +15,27 @@ */ package org.thingsboard.server.mqtt.rpc; -import java.util.Arrays; - import com.datastax.driver.core.utils.UUIDs; -import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.eclipse.paho.client.mqttv3.*; -import org.junit.*; -import org.thingsboard.server.actors.plugin.PluginProcessingContext; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.plugin.PluginMetaData; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.service.security.AccessValidator; + +import java.util.Arrays; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -55,7 +59,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC public void beforeTest() throws Exception { loginSysAdmin(); - asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin(); + asyncContextTimeoutToUseRpcPlugin = 10000L; Tenant tenant = new Tenant(); tenant.setTitle("My tenant"); @@ -131,7 +135,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().isNotFound()); - Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); } @Test @@ -186,7 +190,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, status().isNotFound()); - Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); } private Device getSavedDevice(Device device) throws Exception { @@ -197,13 +201,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); } - private Long getAsyncContextTimeoutToUseRpcPlugin() throws Exception { - TextPageData plugins = doGetTyped("/api/plugin/system?limit=1&textSearch=system rpc plugin", - new TypeReference>(){}); - Long systemRpcPluginTimeout = plugins.getData().iterator().next().getConfiguration().get("defaultTimeout").asLong(); - return systemRpcPluginTimeout + TIME_TO_HANDLE_REQUEST; - } - private static class TestMqttCallback implements MqttCallback { private final MqttAsyncClient client; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java index 7c9c0586f0..f3d69bacd5 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.mqtt.rpc.sql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java new file mode 100644 index 0000000000..bffe4913ef --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2018 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.rules; + +import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; +import org.junit.ClassRule; +import org.junit.extensions.cpsuite.ClasspathSuite; +import org.junit.runner.RunWith; +import org.thingsboard.server.dao.CustomCassandraCQLUnit; +import org.thingsboard.server.dao.CustomSqlUnit; + +import java.util.Arrays; + +@RunWith(ClasspathSuite.class) +@ClasspathSuite.ClassnameFilters({ + "org.thingsboard.server.rules.flow.nosql.*Test", + "org.thingsboard.server.rules.lifecycle.nosql.*Test" +}) +public class RuleEngineNoSqlTestSuite { + + @ClassRule + public static CustomCassandraCQLUnit cassandraUnit = + new CustomCassandraCQLUnit( + Arrays.asList( + new ClassPathCQLDataSet("cassandra/schema.cql", false, false), + new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)), + "cassandra-test.yaml", 30000l); + +} diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java new file mode 100644 index 0000000000..7b13e2fd54 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2018 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.rules; + +import org.junit.ClassRule; +import org.junit.extensions.cpsuite.ClasspathSuite; +import org.junit.runner.RunWith; +import org.thingsboard.server.dao.CustomSqlUnit; + +import java.util.Arrays; + +@RunWith(ClasspathSuite.class) +@ClasspathSuite.ClassnameFilters({ + "org.thingsboard.server.rules.flow.sql.*Test", + "org.thingsboard.server.rules.lifecycle.sql.*Test"}) +public class RuleEngineSqlTestSuite { + + @ClassRule + public static CustomSqlUnit sqlUnit = new CustomSqlUnit( + Arrays.asList("sql/schema.sql", "sql/system-data.sql"), + "sql/drop-all-tables.sql", + "sql-test.properties"); +} diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java new file mode 100644 index 0000000000..c86d49609e --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -0,0 +1,322 @@ +/** + * Copyright © 2016-2018 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.rules.flow; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.actors.service.ActorService; +import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.rule.RuleChainService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Valerii Sosliuk + */ +@Slf4j +public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRuleEngineControllerTest { + + protected Tenant savedTenant; + protected User tenantAdmin; + + @Autowired + protected ActorService actorService; + + @Autowired + protected AttributesService attributesService; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + ruleChainService.deleteRuleChainsByTenantId(savedTenant.getId()); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + if (savedTenant != null) { + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + } + + @Test + public void testRuleChainWithTwoRules() throws Exception { + // Creating Rule Chain + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Simple Rule Chain"); + ruleChain.setTenantId(savedTenant.getId()); + ruleChain.setRoot(true); + ruleChain.setDebugMode(true); + ruleChain = saveRuleChain(ruleChain); + Assert.assertNull(ruleChain.getFirstRuleNodeId()); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("Simple Rule Node 1"); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); + ruleNode1.setConfiguration(mapper.valueToTree(configuration1)); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("Simple Rule Node 2"); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode2.setConfiguration(mapper.valueToTree(configuration2)); + + + metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); + metaData.setFirstNodeIndex(0); + metaData.addConnectionInfo(0, 1, "Success"); + metaData = saveRuleChainMetaData(metaData); + Assert.assertNotNull(metaData); + + ruleChain = getRuleChain(ruleChain.getId()); + Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))); + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))); + + + Thread.sleep(1000); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + "{}", null, null, 0L); + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); + + Thread.sleep(3000); + + TimePageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); + List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + Assert.assertEquals(2, events.size()); + + Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); + + RuleChain finalRuleChain = ruleChain; + RuleNode lastRuleNode = metaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get(); + + eventsPage = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000); + events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + + Assert.assertEquals(2, events.size()); + + inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); + Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText()); + + List unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), ruleChain.getId().getId(), 0L)); + Assert.assertEquals(0, unAckMsgList.size()); + } + + @Test + public void testTwoRuleChainsWithTwoRules() throws Exception { + // Creating Rule Chain + RuleChain rootRuleChain = new RuleChain(); + rootRuleChain.setName("Root Rule Chain"); + rootRuleChain.setTenantId(savedTenant.getId()); + rootRuleChain.setRoot(true); + rootRuleChain.setDebugMode(true); + rootRuleChain = saveRuleChain(rootRuleChain); + Assert.assertNull(rootRuleChain.getFirstRuleNodeId()); + + // Creating Rule Chain + RuleChain secondaryRuleChain = new RuleChain(); + secondaryRuleChain.setName("Secondary Rule Chain"); + secondaryRuleChain.setTenantId(savedTenant.getId()); + secondaryRuleChain.setRoot(false); + secondaryRuleChain.setDebugMode(true); + secondaryRuleChain = saveRuleChain(secondaryRuleChain); + Assert.assertNull(secondaryRuleChain.getFirstRuleNodeId()); + + RuleChainMetaData rootMetaData = new RuleChainMetaData(); + rootMetaData.setRuleChainId(rootRuleChain.getId()); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("Simple Rule Node 1"); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); + ruleNode1.setConfiguration(mapper.valueToTree(configuration1)); + + rootMetaData.setNodes(Collections.singletonList(ruleNode1)); + rootMetaData.setFirstNodeIndex(0); + rootMetaData.addRuleChainConnectionInfo(0, secondaryRuleChain.getId(), "Success", mapper.createObjectNode()); + rootMetaData = saveRuleChainMetaData(rootMetaData); + Assert.assertNotNull(rootMetaData); + + rootRuleChain = getRuleChain(rootRuleChain.getId()); + Assert.assertNotNull(rootRuleChain.getFirstRuleNodeId()); + + + RuleChainMetaData secondaryMetaData = new RuleChainMetaData(); + secondaryMetaData.setRuleChainId(secondaryRuleChain.getId()); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("Simple Rule Node 2"); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode2.setConfiguration(mapper.valueToTree(configuration2)); + + secondaryMetaData.setNodes(Collections.singletonList(ruleNode2)); + secondaryMetaData.setFirstNodeIndex(0); + secondaryMetaData = saveRuleChainMetaData(secondaryMetaData); + Assert.assertNotNull(secondaryMetaData); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey1", "serverAttributeValue1"), System.currentTimeMillis()))); + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey2", "serverAttributeValue2"), System.currentTimeMillis()))); + + + Thread.sleep(1000); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + "{}", null, null, 0L); + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); + + Thread.sleep(3000); + + TimePageData eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000); + List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + + Assert.assertEquals(2, events.size()); + + Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(rootRuleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(rootRuleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); + + RuleChain finalRuleChain = rootRuleChain; + RuleNode lastRuleNode = secondaryMetaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get(); + + eventsPage = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000); + events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + + + Assert.assertEquals(2, events.size()); + + inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(lastRuleNode.getId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue1", getMetadata(outEvent).get("ss_serverAttributeKey1").asText()); + Assert.assertEquals("serverAttributeValue2", getMetadata(outEvent).get("ss_serverAttributeKey2").asText()); + + List unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), rootRuleChain.getId().getId(), 0L)); + Assert.assertEquals(0, unAckMsgList.size()); + + unAckMsgList = Lists.newArrayList(msgQueueService.findUnprocessed(savedTenant.getId(), secondaryRuleChain.getId().getId(), 0L)); + Assert.assertEquals(0, unAckMsgList.size()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/nosql/PluginControllerNoSqlTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/nosql/RuleEngineFlowNoSqlIntegrationTest.java similarity index 72% rename from application/src/test/java/org/thingsboard/server/controller/nosql/PluginControllerNoSqlTest.java rename to application/src/test/java/org/thingsboard/server/rules/flow/nosql/RuleEngineFlowNoSqlIntegrationTest.java index 48815e6057..d073a1de4a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/nosql/PluginControllerNoSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/nosql/RuleEngineFlowNoSqlIntegrationTest.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.controller.nosql; +package org.thingsboard.server.rules.flow.nosql; -import org.thingsboard.server.controller.BasePluginControllerTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; +import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; /** - * Created by Valerii Sosliuk on 6/28/2017. + * Created by Valerii Sosliuk on 8/22/2017. */ @DaoNoSqlTest -public class PluginControllerNoSqlTest extends BasePluginControllerTest { +public class RuleEngineFlowNoSqlIntegrationTest extends AbstractRuleEngineFlowIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/PluginControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/sql/RuleEngineFlowSqlIntegrationTest.java similarity index 66% rename from application/src/test/java/org/thingsboard/server/controller/sql/PluginControllerSqlTest.java rename to application/src/test/java/org/thingsboard/server/rules/flow/sql/RuleEngineFlowSqlIntegrationTest.java index fbb63385e7..24f288ca59 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/PluginControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/sql/RuleEngineFlowSqlIntegrationTest.java @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.controller.sql; +package org.thingsboard.server.rules.flow.sql; -import org.thingsboard.server.controller.BasePluginControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; +import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; /** - * Created by Valerii Sosliuk on 6/28/2017. + * Created by Valerii Sosliuk on 8/22/2017. */ @DaoSqlTest -public class PluginControllerSqlTest extends BasePluginControllerTest { +public class RuleEngineFlowSqlIntegrationTest extends AbstractRuleEngineFlowIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java new file mode 100644 index 0000000000..7ac0789383 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -0,0 +1,234 @@ +/** + * Copyright © 2016-2018 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.rules.lifecycle; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.actors.service.ActorService; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; +import org.thingsboard.server.controller.AbstractRuleEngineControllerTest; +import org.thingsboard.server.dao.attributes.AttributesService; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Valerii Sosliuk + */ +@Slf4j +public abstract class AbstractRuleEngineLifecycleIntegrationTest extends AbstractRuleEngineControllerTest { + + protected Tenant savedTenant; + protected User tenantAdmin; + + @Autowired + protected ActorService actorService; + + @Autowired + protected AttributesService attributesService; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + ruleChainService.deleteRuleChainsByTenantId(savedTenant.getId()); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + if (savedTenant != null) { + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + } + + @Test + public void testRuleChainWithOneRule() throws Exception { + // Creating Rule Chain + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Simple Rule Chain"); + ruleChain.setTenantId(savedTenant.getId()); + ruleChain.setRoot(true); + ruleChain.setDebugMode(true); + ruleChain = saveRuleChain(ruleChain); + Assert.assertNull(ruleChain.getFirstRuleNodeId()); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode = new RuleNode(); + ruleNode.setName("Simple Rule Node"); + ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); + configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); + ruleNode.setConfiguration(mapper.valueToTree(configuration)); + + metaData.setNodes(Collections.singletonList(ruleNode)); + metaData.setFirstNodeIndex(0); + + metaData = saveRuleChainMetaData(metaData); + Assert.assertNotNull(metaData); + + ruleChain = getRuleChain(ruleChain.getId()); + Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))); + + Thread.sleep(1000); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + "{}", + null, null, 0L); + actorService.onMsg(new ServiceToRuleEngineMsg(savedTenant.getId(), tbMsg)); + + Thread.sleep(3000); + + TimePageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); + List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + + Assert.assertEquals(2, events.size()); + + Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText()); + } + + @Test + public void testRuleChainWithOneRuleAndMsgFromQueue() throws Exception { + // Creating Rule Chain + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Simple Rule Chain"); + ruleChain.setTenantId(savedTenant.getId()); + ruleChain.setRoot(true); + ruleChain.setDebugMode(true); + ruleChain = saveRuleChain(ruleChain); + Assert.assertNull(ruleChain.getFirstRuleNodeId()); + + // Saving the device + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + device = doPost("/api/device", device, Device.class); + + attributesService.save(device.getId(), DataConstants.SERVER_SCOPE, + Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis()))); + + // Pushing Message to the system + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), + "CUSTOM", + device.getId(), + new TbMsgMetaData(), + "{}", + ruleChain.getId(), null, 0L); + msgQueueService.put(device.getTenantId(), tbMsg, ruleChain.getId().getId(), 0L); + + Thread.sleep(1000); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode = new RuleNode(); + ruleNode.setName("Simple Rule Node"); + ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); + configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); + ruleNode.setConfiguration(mapper.valueToTree(configuration)); + + metaData.setNodes(Collections.singletonList(ruleNode)); + metaData.setFirstNodeIndex(0); + + metaData = saveRuleChainMetaData(metaData); + Assert.assertNotNull(metaData); + + ruleChain = getRuleChain(ruleChain.getId()); + Assert.assertNotNull(ruleChain.getFirstRuleNodeId()); + + Thread.sleep(3000); + + TimePageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); + List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + + Assert.assertEquals(2, events.size()); + + Event inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), inEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), inEvent.getBody().get("entityId").asText()); + + Event outEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.OUT)).findFirst().get(); + Assert.assertEquals(ruleChain.getFirstRuleNodeId(), outEvent.getEntityId()); + Assert.assertEquals(device.getId().getId().toString(), outEvent.getBody().get("entityId").asText()); + + Assert.assertEquals("serverAttributeValue", getMetadata(outEvent).get("ss_serverAttributeKey").asText()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/nosql/RuleControllerNoSqlTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/nosql/RuleEngineLifecycleNoSqlIntegrationTest.java similarity index 70% rename from application/src/test/java/org/thingsboard/server/controller/nosql/RuleControllerNoSqlTest.java rename to application/src/test/java/org/thingsboard/server/rules/lifecycle/nosql/RuleEngineLifecycleNoSqlIntegrationTest.java index 0557d5d3d9..6f66359f62 100644 --- a/application/src/test/java/org/thingsboard/server/controller/nosql/RuleControllerNoSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/nosql/RuleEngineLifecycleNoSqlIntegrationTest.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.controller.nosql; +package org.thingsboard.server.rules.lifecycle.nosql; -import org.thingsboard.server.controller.BaseRuleControllerTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; +import org.thingsboard.server.rules.lifecycle.AbstractRuleEngineLifecycleIntegrationTest; /** - * Created by Valerii Sosliuk on 6/28/2017. + * Created by Valerii Sosliuk on 8/22/2017. */ @DaoNoSqlTest -public class RuleControllerNoSqlTest extends BaseRuleControllerTest { +public class RuleEngineLifecycleNoSqlIntegrationTest extends AbstractRuleEngineLifecycleIntegrationTest { } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/sql/PluginServiceSqlTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/sql/RuleEngineLifecycleSqlIntegrationTest.java similarity index 64% rename from dao/src/test/java/org/thingsboard/server/dao/service/plugin/sql/PluginServiceSqlTest.java rename to application/src/test/java/org/thingsboard/server/rules/lifecycle/sql/RuleEngineLifecycleSqlIntegrationTest.java index 4bd96f3b5a..c74c515124 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/sql/PluginServiceSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/sql/RuleEngineLifecycleSqlIntegrationTest.java @@ -13,11 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.plugin.sql; +package org.thingsboard.server.rules.lifecycle.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.service.plugin.BasePluginServiceTest; +import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; +import org.thingsboard.server.rules.lifecycle.AbstractRuleEngineLifecycleIntegrationTest; +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ @DaoSqlTest -public class PluginServiceSqlTest extends BasePluginServiceTest { +public class RuleEngineLifecycleSqlIntegrationTest extends AbstractRuleEngineLifecycleIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java b/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java index ed3750d6ca..ba2bb65acd 100644 --- a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java +++ b/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java @@ -22,7 +22,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; -import org.thingsboard.server.exception.ThingsboardException; +import org.thingsboard.rule.engine.api.MailService; +import org.thingsboard.server.common.data.exception.ThingsboardException; @Profile("test") @Configuration diff --git a/application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java b/application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java new file mode 100644 index 0000000000..ea7038442d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java @@ -0,0 +1,171 @@ +/** + * Copyright © 2016-2018 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.script; + +import com.datastax.driver.core.utils.UUIDs; +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.rule.engine.api.ScriptEngine; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import javax.script.ScriptException; + +import java.util.Set; + +import static org.junit.Assert.*; + +public class RuleNodeJsScriptEngineTest { + + private ScriptEngine scriptEngine; + private TestNashornJsSandboxService jsSandboxService; + + @Before + public void beforeTest() throws Exception { + jsSandboxService = new TestNashornJsSandboxService(false, 1, 100, 3); + } + + @After + public void afterTest() throws Exception { + jsSandboxService.stop(); + } + + @Test + public void msgCanBeUpdated() throws ScriptException { + String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function); + + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "7"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + + TbMsg actual = scriptEngine.executeUpdate(msg); + assertEquals("70", actual.getMetaData().getValue("temp")); + scriptEngine.destroy(); + } + + @Test + public void newAttributesCanBeAddedInMsg() throws ScriptException { + String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "7"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + + TbMsg actual = scriptEngine.executeUpdate(msg); + assertEquals("94", actual.getMetaData().getValue("newAttr")); + scriptEngine.destroy(); + } + + @Test + public void payloadCanBeUpdated() throws ScriptException { + String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "7"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + + TbMsg actual = scriptEngine.executeUpdate(msg); + + String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}"; + assertEquals(expectedJson, actual.getData()); + scriptEngine.destroy(); + } + + @Test + public void metadataAccessibleForFilter() throws ScriptException { + String function = "return metadata.humidity < 15;"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "7"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + assertFalse(scriptEngine.executeFilter(msg)); + scriptEngine.destroy(); + } + + @Test + public void dataAccessibleForFilter() throws ScriptException { + String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, function); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "7"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + assertTrue(scriptEngine.executeFilter(msg)); + scriptEngine.destroy(); + } + + @Test + public void dataAccessibleForSwitch() throws ScriptException { + String jsCode = "function nextRelation(metadata, msg) {\n" + + " if(msg.passed == 5 && metadata.temp == 10)\n" + + " return 'one'\n" + + " else\n" + + " return 'two';\n" + + "};\n" + + "\n" + + "return nextRelation(metadata, msg);"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "10"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + Set actual = scriptEngine.executeSwitch(msg); + assertEquals(Sets.newHashSet("one"), actual); + scriptEngine.destroy(); + } + + @Test + public void multipleRelationsReturnedFromSwitch() throws ScriptException { + String jsCode = "function nextRelation(metadata, msg) {\n" + + " if(msg.passed == 5 && metadata.temp == 10)\n" + + " return ['three', 'one']\n" + + " else\n" + + " return 'two';\n" + + "};\n" + + "\n" + + "return nextRelation(metadata, msg);"; + scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, jsCode); + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("temp", "10"); + metaData.putValue("humidity", "99"); + String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; + + TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson, null, null, 0L); + Set actual = scriptEngine.executeSwitch(msg); + assertEquals(Sets.newHashSet("one", "three"), actual); + scriptEngine.destroy(); + } + +} \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/service/script/TestNashornJsSandboxService.java b/application/src/test/java/org/thingsboard/server/service/script/TestNashornJsSandboxService.java new file mode 100644 index 0000000000..731c4bb147 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/script/TestNashornJsSandboxService.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2018 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.script; + +import com.google.common.util.concurrent.ListenableFuture; +import delight.nashornsandbox.NashornSandbox; +import delight.nashornsandbox.NashornSandboxes; + +import javax.script.ScriptException; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class TestNashornJsSandboxService extends AbstractNashornJsSandboxService { + + private boolean useJsSandbox; + private final int monitorThreadPoolSize; + private final long maxCpuTime; + private final int maxErrors; + + public TestNashornJsSandboxService(boolean useJsSandbox, int monitorThreadPoolSize, long maxCpuTime, int maxErrors) { + this.useJsSandbox = useJsSandbox; + this.monitorThreadPoolSize = monitorThreadPoolSize; + this.maxCpuTime = maxCpuTime; + this.maxErrors = maxErrors; + init(); + } + + @Override + protected boolean useJsSandbox() { + return useJsSandbox; + } + + @Override + protected int getMonitorThreadPoolSize() { + return monitorThreadPoolSize; + } + + @Override + protected long getMaxCpuTime() { + return maxCpuTime; + } + + @Override + protected int getMaxErrors() { + return maxErrors; + } +} diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index cfa0c586a9..97c67491f1 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -35,5 +35,4 @@ public class SystemSqlTestSuite { "sql/drop-all-tables.sql", "sql-test.properties"); - } diff --git a/common/data/pom.xml b/common/data/pom.xml index 577fcff733..793d8892c5 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 03115a9940..078c97b1c0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; -public class Customer extends ContactBased implements HasName { +public class Customer extends ContactBased implements HasName, HasTenantId { private static final long serialVersionUID = -1599722990298929275L; 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 a776d7b6ce..6b1c4a481e 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 @@ -37,7 +37,25 @@ public class DataConstants { public static final String ERROR = "ERROR"; public static final String LC_EVENT = "LC_EVENT"; public static final String STATS = "STATS"; + public static final String DEBUG_RULE_NODE = "DEBUG_RULE_NODE"; public static final String ONEWAY = "ONEWAY"; public static final String TWOWAY = "TWOWAY"; + + public static final String IN = "IN"; + public static final String OUT = "OUT"; + + public static final String INACTIVITY_EVENT = "INACTIVITY_EVENT"; + public static final String CONNECT_EVENT = "CONNECT_EVENT"; + public static final String DISCONNECT_EVENT = "DISCONNECT_EVENT"; + public static final String ACTIVITY_EVENT = "ACTIVITY_EVENT"; + + public static final String ENTITY_CREATED = "ENTITY_CREATED"; + public static final String ENTITY_UPDATED = "ENTITY_UPDATED"; + public static final String ENTITY_DELETED = "ENTITY_DELETED"; + public static final String ENTITY_ASSIGNED = "ENTITY_ASSIGNED"; + public static final String ENTITY_UNASSIGNED = "ENTITY_UNASSIGNED"; + public static final String ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED"; + public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 13fa011d9d..95662c1292 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; @EqualsAndHashCode(callSuper = true) -public class Device extends SearchTextBasedWithAdditionalInfo implements HasName { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { private static final long serialVersionUID = 2807343040519543363L; 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 05b558e6d7..fe9c018718 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, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE; } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/ConfigurableComponent.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasCustomerId.java similarity index 78% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/ConfigurableComponent.java rename to common/data/src/main/java/org/thingsboard/server/common/data/HasCustomerId.java index 14aa025cac..e89eba8bbb 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/ConfigurableComponent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasCustomerId.java @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.component; +package org.thingsboard.server.common.data; -/** - * @author Andrew Shvayka - */ -public interface ConfigurableComponent { +import org.thingsboard.server.common.data.id.CustomerId; - void init(T configuration); +public interface HasCustomerId { + CustomerId getCustomerId(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleAwareMsg.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasTenantId.java similarity index 79% rename from common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleAwareMsg.java rename to common/data/src/main/java/org/thingsboard/server/common/data/HasTenantId.java index b14e882cca..ebba003cc8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleAwareMsg.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasTenantId.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.msg.aware; +package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.id.TenantId; -public interface RuleAwareMsg { +public interface HasTenantId { - RuleId getRuleId(); - + TenantId getTenantId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index c893d644d9..15c52dc49c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -15,9 +15,11 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.EqualsAndHashCode; 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.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; @@ -25,7 +27,7 @@ import org.thingsboard.server.common.data.security.Authority; import com.fasterxml.jackson.databind.JsonNode; @EqualsAndHashCode(callSuper = true) -public class User extends SearchTextBasedWithAdditionalInfo implements HasName { +public class User extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { private static final long serialVersionUID = 8250339805336035966L; @@ -138,4 +140,18 @@ public class User extends SearchTextBasedWithAdditionalInfo implements H return builder.toString(); } + @JsonIgnore + public boolean isSystemAdmin() { + return tenantId == null || EntityId.NULL_UUID.equals(tenantId.getId()); + } + + @JsonIgnore + public boolean isTenantAdmin() { + return !isSystemAdmin() && (customerId == null || EntityId.NULL_UUID.equals(customerId.getId())); + } + + @JsonIgnore + public boolean isCustomerUser() { + return !isSystemAdmin() && !isTenantAdmin(); + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 70f5042a75..125406c4a1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -22,6 +22,7 @@ import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -31,7 +32,7 @@ import org.thingsboard.server.common.data.id.TenantId; @Data @Builder @AllArgsConstructor -public class Alarm extends BaseData implements HasName { +public class Alarm extends BaseData implements HasName, HasTenantId { private TenantId tenantId; private String type; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index cc3c111724..c7b246c1ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -17,16 +17,13 @@ package org.thingsboard.server.common.data.asset; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.HasAdditionalInfo; -import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.SearchTextBased; -import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) -public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName { +public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { private static final long serialVersionUID = 2807343040519543363L; diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java rename to common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java index e78d4f208a..5b7cfb9864 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.exception; +package org.thingsboard.server.common.data.exception; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java rename to common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java index fcbd65eb27..fe4bd502e0 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.exception; +package org.thingsboard.server.common.data.exception; public class ThingsboardException extends Exception { 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 e45cb91613..0ecc7c639f 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 @@ -33,6 +33,10 @@ public class EntityIdFactory { return getByTypeAndUuid(EntityType.valueOf(type), uuid); } + public static EntityId getByTypeAndUuid(EntityType type, String uuid) { + return getByTypeAndUuid(type, UUID.fromString(uuid)); + } + public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) { switch (type) { case TENANT: @@ -41,10 +45,6 @@ public class EntityIdFactory { return new CustomerId(uuid); case USER: return new UserId(uuid); - case RULE: - return new RuleId(uuid); - case PLUGIN: - return new PluginId(uuid); case DASHBOARD: return new DashboardId(uuid); case DEVICE: @@ -53,6 +53,10 @@ public class EntityIdFactory { return new AssetId(uuid); case ALARM: return new AlarmId(uuid); + case RULE_CHAIN: + return new RuleChainId(uuid); + case RULE_NODE: + return new RuleNodeId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java index 6ac513602a..e8328072c7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java @@ -17,9 +17,10 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.Serializable; import java.util.UUID; -public abstract class IdBased { +public abstract class IdBased implements Serializable { protected I id; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/PluginId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java similarity index 83% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/PluginId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java index d3ce4668ac..a1a5075d27 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/PluginId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java @@ -22,18 +22,16 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public final class PluginId extends UUIDBased implements EntityId { - - private static final long serialVersionUID = 1L; +public class RuleChainId extends UUIDBased implements EntityId { @JsonCreator - public PluginId(@JsonProperty("id") UUID id) { + public RuleChainId(@JsonProperty("id") UUID id) { super(id); } @JsonIgnore @Override public EntityType getEntityType() { - return EntityType.PLUGIN; + return EntityType.RULE_CHAIN; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java similarity index 86% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/RuleId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java index 6f14042378..ec5ecce0d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java @@ -22,16 +22,16 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class RuleId extends UUIDBased implements EntityId { +public class RuleNodeId extends UUIDBased implements EntityId { @JsonCreator - public RuleId(@JsonProperty("id") UUID id) { + public RuleNodeId(@JsonProperty("id") UUID id) { super(id); } @JsonIgnore @Override public EntityType getEntityType() { - return EntityType.RULE; + return EntityType.RULE_NODE; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java index 34f8c3aed6..ffd78225c4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.NoSuchElementException; import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; public class PageDataIterable> implements Iterable, Iterator { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java index 45fb590ed6..13351476e7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentType.java @@ -20,6 +20,6 @@ package org.thingsboard.server.common.data.plugin; */ public enum ComponentType { - FILTER, PROCESSOR, ACTION, PLUGIN + ENRICHMENT, FILTER, TRANSFORMATION, ACTION, EXTERNAL } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java deleted file mode 100644 index 8576264d5b..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.EqualsAndHashCode; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; - -import com.fasterxml.jackson.databind.JsonNode; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -@EqualsAndHashCode(callSuper = true) -@Slf4j -public class PluginMetaData extends SearchTextBasedWithAdditionalInfo implements HasName { - - private static final long serialVersionUID = 1L; - - private String apiToken; - private TenantId tenantId; - private String name; - private String clazz; - private boolean publicAccess; - private ComponentLifecycleState state; - private transient JsonNode configuration; - @JsonIgnore - private byte[] configurationBytes; - - - public PluginMetaData() { - super(); - } - - public PluginMetaData(PluginId id) { - super(id); - } - - public PluginMetaData(PluginMetaData plugin) { - super(plugin); - this.apiToken = plugin.getApiToken(); - this.tenantId = plugin.getTenantId(); - this.name = plugin.getName(); - this.clazz = plugin.getClazz(); - this.publicAccess = plugin.isPublicAccess(); - this.state = plugin.getState(); - this.configuration = plugin.getConfiguration(); - } - - @Override - public String getSearchText() { - return getName(); - } - - public String getApiToken() { - return apiToken; - } - - public void setApiToken(String apiToken) { - this.apiToken = apiToken; - } - - public TenantId getTenantId() { - return tenantId; - } - - public void setTenantId(TenantId tenantId) { - this.tenantId = tenantId; - } - - @Override - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getClazz() { - return clazz; - } - - public void setClazz(String clazz) { - this.clazz = clazz; - } - - public JsonNode getConfiguration() { - return getJson(() -> configuration, () -> configurationBytes); - } - - public void setConfiguration(JsonNode data) { - setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); - } - - public boolean isPublicAccess() { - return publicAccess; - } - - public void setPublicAccess(boolean publicAccess) { - this.publicAccess = publicAccess; - } - - public void setState(ComponentLifecycleState state) { - this.state = state; - } - - public ComponentLifecycleState getState() { - return state; - } - - @Override - public String toString() { - return "PluginMetaData [apiToken=" + apiToken + ", tenantId=" + tenantId + ", name=" + name + ", clazz=" + clazz + ", publicAccess=" + publicAccess - + ", configuration=" + configuration + "]"; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index 90e0253370..55990552b0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java @@ -19,6 +19,8 @@ public enum RelationTypeGroup { COMMON, ALARM, - DASHBOARD + DASHBOARD, + RULE_CHAIN, + RULE_NODE } diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/cmd/RpcRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcRequest.java similarity index 92% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/cmd/RpcRequest.java rename to common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcRequest.java index 31a6911be9..a915df8ffc 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/cmd/RpcRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.plugin.rpc.cmd; +package org.thingsboard.server.common.data.rpc; import lombok.Data; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestBody.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/ToDeviceRpcRequestBody.java similarity index 93% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestBody.java rename to common/data/src/main/java/org/thingsboard/server/common/data/rpc/ToDeviceRpcRequestBody.java index b6503363bd..73eb83e7f7 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestBody.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/ToDeviceRpcRequestBody.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.common.data.rpc; import lombok.Data; diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java similarity index 77% rename from extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java rename to common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java index 3c4e4b0256..0c9fd5feb5 100644 --- a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/NodeConnectionInfo.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.sns.plugin; +package org.thingsboard.server.common.data.rule; import lombok.Data; /** - * Created by Valerii Sosliuk on 11/5/2017. + * Created by ashvayka on 21.03.18. */ @Data -public class SnsPluginConfiguration { - - private String accessKeyId; - private String secretAccessKey; - private String region; - +public class NodeConnectionInfo { + private int fromIndex; + private int toIndex; + private String type; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java new file mode 100644 index 0000000000..218061adb1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -0,0 +1,81 @@ +/** + * Copyright © 2016-2018 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.rule; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@EqualsAndHashCode(callSuper = true) +@Slf4j +public class RuleChain extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { + + private static final long serialVersionUID = -5656679015121935465L; + + private TenantId tenantId; + private String name; + private RuleNodeId firstRuleNodeId; + private boolean root; + private boolean debugMode; + private transient JsonNode configuration; + @JsonIgnore + private byte[] configurationBytes; + + public RuleChain() { + super(); + } + + public RuleChain(RuleChainId id) { + super(id); + } + + public RuleChain(RuleChain ruleChain) { + super(ruleChain); + this.tenantId = ruleChain.getTenantId(); + this.name = ruleChain.getName(); + this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); + this.root = ruleChain.isRoot(); + this.setConfiguration(ruleChain.getConfiguration()); + } + + @Override + public String getSearchText() { + return getName(); + } + + @Override + public String getName() { + return name; + } + + public JsonNode getConfiguration() { + return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes); + } + + public void setConfiguration(JsonNode data) { + setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); + } + +} diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailActionConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java similarity index 64% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailActionConfiguration.java rename to common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java index 2b27275d35..a537fe4147 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailActionConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainConnectionInfo.java @@ -13,22 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.action.mail; +package org.thingsboard.server.common.data.rule; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; +import org.thingsboard.server.common.data.id.RuleChainId; /** - * @author Andrew Shvayka + * Created by ashvayka on 21.03.18. */ @Data -public class SendMailActionConfiguration { - - private String sendFlag; - - private String fromTemplate; - private String toTemplate; - private String ccTemplate; - private String bccTemplate; - private String subjectTemplate; - private String bodyTemplate; +public class RuleChainConnectionInfo { + private int fromIndex; + private RuleChainId targetRuleChainId; + private JsonNode additionalInfo; + private String type; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java new file mode 100644 index 0000000000..7ecd6df3cc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2018 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.rule; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import org.thingsboard.server.common.data.id.RuleChainId; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by igor on 3/13/18. + */ +@Data +public class RuleChainMetaData { + + private RuleChainId ruleChainId; + + private Integer firstNodeIndex; + + private List nodes; + + private List connections; + + private List ruleChainConnections; + + public void addConnectionInfo(int fromIndex, int toIndex, String type) { + NodeConnectionInfo connectionInfo = new NodeConnectionInfo(); + connectionInfo.setFromIndex(fromIndex); + connectionInfo.setToIndex(toIndex); + connectionInfo.setType(type); + if (connections == null) { + connections = new ArrayList<>(); + } + connections.add(connectionInfo); + } + public void addRuleChainConnectionInfo(int fromIndex, RuleChainId targetRuleChainId, String type, JsonNode additionalInfo) { + RuleChainConnectionInfo connectionInfo = new RuleChainConnectionInfo(); + connectionInfo.setFromIndex(fromIndex); + connectionInfo.setTargetRuleChainId(targetRuleChainId); + connectionInfo.setType(type); + connectionInfo.setAdditionalInfo(additionalInfo); + if (ruleChainConnections == null) { + ruleChainConnections = new ArrayList<>(); + } + ruleChainConnections.add(connectionInfo); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java deleted file mode 100644 index 98adeb7742..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; - -@Data -@EqualsAndHashCode(callSuper = true) -@Slf4j -public class RuleMetaData extends SearchTextBasedWithAdditionalInfo implements HasName { - - private static final long serialVersionUID = -5656679015122935465L; - - private TenantId tenantId; - private String name; - private ComponentLifecycleState state; - private int weight; - private String pluginToken; - private transient JsonNode filters; - private transient JsonNode processor; - private transient JsonNode action; - @JsonIgnore - private byte[] filtersBytes; - @JsonIgnore - private byte[] processorBytes; - @JsonIgnore - private byte[] actionBytes; - - - public RuleMetaData() { - super(); - } - - public RuleMetaData(RuleId id) { - super(id); - } - - public RuleMetaData(RuleMetaData rule) { - super(rule); - this.tenantId = rule.getTenantId(); - this.name = rule.getName(); - this.state = rule.getState(); - this.weight = rule.getWeight(); - this.pluginToken = rule.getPluginToken(); - this.setFilters(rule.getFilters()); - this.setProcessor(rule.getProcessor()); - this.setAction(rule.getAction()); - } - - @Override - public String getSearchText() { - return getName(); - } - - @Override - public String getName() { - return name; - } - - public JsonNode getFilters() { - return SearchTextBasedWithAdditionalInfo.getJson(() -> filters, () -> filtersBytes); - } - - public JsonNode getProcessor() { - return SearchTextBasedWithAdditionalInfo.getJson(() -> processor, () -> processorBytes); - } - - public JsonNode getAction() { - return SearchTextBasedWithAdditionalInfo.getJson(() -> action, () -> actionBytes); - } - - public void setFilters(JsonNode data) { - setJson(data, json -> this.filters = json, bytes -> this.filtersBytes = bytes); - } - - public void setProcessor(JsonNode data) { - setJson(data, json -> this.processor = json, bytes -> this.processorBytes = bytes); - } - - public void setAction(JsonNode data) { - setJson(data, json -> this.action = json, bytes -> this.actionBytes = bytes); - } - - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java new file mode 100644 index 0000000000..808166ba99 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2018 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.rule; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@EqualsAndHashCode(callSuper = true) +@Slf4j +public class RuleNode extends SearchTextBasedWithAdditionalInfo implements HasName { + + private static final long serialVersionUID = -5656679015121235465L; + + private RuleChainId ruleChainId; + private String type; + private String name; + private boolean debugMode; + private transient JsonNode configuration; + @JsonIgnore + private byte[] configurationBytes; + + public RuleNode() { + super(); + } + + public RuleNode(RuleNodeId id) { + super(id); + } + + public RuleNode(RuleNode ruleNode) { + super(ruleNode); + this.ruleChainId = ruleNode.getRuleChainId(); + this.type = ruleNode.getType(); + this.name = ruleNode.getName(); + this.debugMode = ruleNode.isDebugMode(); + this.setConfiguration(ruleNode.getConfiguration()); + } + + @Override + public String getSearchText() { + return getName(); + } + + @Override + public String getName() { + return name; + } + + public JsonNode getConfiguration() { + return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes); + } + + public void setConfiguration(JsonNode data) { + setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); + } + +} diff --git a/common/message/pom.xml b/common/message/pom.xml index 9e97d342c1..c24cfd3e34 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT common org.thingsboard.common @@ -56,6 +56,11 @@ ch.qos.logback logback-classic + + com.google.protobuf + protobuf-java + provided + junit junit @@ -70,6 +75,10 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java new file mode 100644 index 0000000000..7702788704 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java @@ -0,0 +1,106 @@ +/** + * Copyright © 2016-2018 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.msg; + +/** + * Created by ashvayka on 15.03.18. + */ +//TODO: add all "See" references +public enum MsgType { + + /** + * ADDED/UPDATED/DELETED events for server nodes. + * + * See {@link org.thingsboard.server.common.msg.cluster.ClusterEventMsg} + */ + CLUSTER_EVENT_MSG, + + /** + * All messages, could be send to cluster + */ + SEND_TO_CLUSTER_MSG, + + /** + * ADDED/UPDATED/DELETED events for main entities. + * + * See {@link org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg} + */ + COMPONENT_LIFE_CYCLE_MSG, + + /** + * Misc messages from the REST API/SERVICE layer to the new rule engine. + * + * See {@link org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg} + */ + SERVICE_TO_RULE_ENGINE_MSG, + + /** + * Message that is sent by RuleChainActor to RuleActor with command to process TbMsg. + */ + RULE_CHAIN_TO_RULE_MSG, + + /** + * Message that is sent by RuleChainActor to other RuleChainActor with command to process TbMsg. + */ + RULE_CHAIN_TO_RULE_CHAIN_MSG, + + /** + * Message that is sent by RuleActor to RuleChainActor with command to process TbMsg by next nodes in chain. + */ + RULE_TO_RULE_CHAIN_TELL_NEXT_MSG, + + /** + * Message that is sent by RuleActor implementation to RuleActor itself to log the error. + */ + RULE_TO_SELF_ERROR_MSG, + + /** + * Message that is sent by RuleActor implementation to RuleActor itself to process the message. + */ + RULE_TO_SELF_MSG, + + /** + * Message that is sent by Session Actor to Device Actor. Represents messages from the device itself. + */ + DEVICE_SESSION_TO_DEVICE_ACTOR_MSG, + + DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG, + + DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG, + + DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG, + + DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG, + + SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG, + + DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG, + + DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG, + + DEVICE_ACTOR_QUEUE_TIMEOUT_MSG, + + /** + * Message that is sent from the Device Actor to Rule Engine. Requires acknowledgement + */ + DEVICE_ACTOR_TO_RULE_ENGINE_MSG, + + /** + * Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue. + */ + RULE_ENGINE_QUEUE_PUT_ACK_MSG, ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG, TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG, SESSION_TIMEOUT_MSG, SESSION_CTRL_MSG; + +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java deleted file mode 100644 index ee352ad722..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2018 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.msg; - -import org.thingsboard.server.common.data.rule.Scope; -import org.thingsboard.server.common.data.rule.RuleType; -import org.thingsboard.server.common.msg.aware.RuleAwareMsg; - -/** - * Message that is used to deliver some data to the rule instance. - * For example: aggregated statistics or command decoded from http request. - * - * @author ashvayka - * - * @param - payload - */ -public interface RuleMsg extends RuleAwareMsg { - - Scope getRuleLevel(); - - RuleType getRuleType(); - - V getPayload(); - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java new file mode 100644 index 0000000000..c361c11c0e --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2018 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.msg; + +/** + * Created by ashvayka on 15.03.18. + */ +public interface TbActorMsg { + + MsgType getMsgType(); + +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java new file mode 100644 index 0000000000..a6eb64e544 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2018 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.msg; + +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.msg.gen.MsgProtos; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Created by ashvayka on 13.01.18. + */ +@Data +@AllArgsConstructor +public final class TbMsg implements Serializable { + + private final UUID id; + private final String type; + private final EntityId originator; + private final TbMsgMetaData metaData; + private final TbMsgDataType dataType; + private final String data; + + //The following fields are not persisted to DB, because they can always be recovered from the context; + private final RuleChainId ruleChainId; + private final RuleNodeId ruleNodeId; + private final long clusterPartition; + + public TbMsg(UUID id, String type, EntityId originator, TbMsgMetaData metaData, String data, + RuleChainId ruleChainId, RuleNodeId ruleNodeId, long clusterPartition) { + this.id = id; + this.type = type; + this.originator = originator; + this.metaData = metaData; + this.data = data; + this.dataType = TbMsgDataType.JSON; + this.ruleChainId = ruleChainId; + this.ruleNodeId = ruleNodeId; + this.clusterPartition = clusterPartition; + } + + public static ByteBuffer toBytes(TbMsg msg) { + MsgProtos.TbMsgProto.Builder builder = MsgProtos.TbMsgProto.newBuilder(); + builder.setId(msg.getId().toString()); + builder.setType(msg.getType()); + builder.setEntityType(msg.getOriginator().getEntityType().name()); + builder.setEntityIdMSB(msg.getOriginator().getId().getMostSignificantBits()); + builder.setEntityIdLSB(msg.getOriginator().getId().getLeastSignificantBits()); + + if (msg.getRuleChainId() != null) { + builder.setRuleChainIdMSB(msg.getRuleChainId().getId().getMostSignificantBits()); + builder.setRuleChainIdLSB(msg.getRuleChainId().getId().getLeastSignificantBits()); + } + + if (msg.getRuleNodeId() != null) { + builder.setRuleNodeIdMSB(msg.getRuleNodeId().getId().getMostSignificantBits()); + builder.setRuleNodeIdLSB(msg.getRuleNodeId().getId().getLeastSignificantBits()); + } + + if (msg.getMetaData() != null) { + builder.setMetaData(MsgProtos.TbMsgMetaDataProto.newBuilder().putAllData(msg.getMetaData().getData()).build()); + } + + builder.setDataType(msg.getDataType().ordinal()); + builder.setData(msg.getData()); + byte[] bytes = builder.build().toByteArray(); + return ByteBuffer.wrap(bytes); + } + + public static TbMsg fromBytes(ByteBuffer buffer) { + try { + MsgProtos.TbMsgProto proto = MsgProtos.TbMsgProto.parseFrom(buffer.array()); + TbMsgMetaData metaData = new TbMsgMetaData(proto.getMetaData().getDataMap()); + EntityId entityId = EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())); + RuleChainId ruleChainId = new RuleChainId(new UUID(proto.getRuleChainIdMSB(), proto.getRuleChainIdLSB())); + RuleNodeId ruleNodeId = null; + if(proto.getRuleNodeIdMSB() != 0L && proto.getRuleNodeIdLSB() != 0L) { + ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); + } + TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; + return new TbMsg(UUID.fromString(proto.getId()), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getClusterPartition()); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException("Could not parse protobuf for TbMsg", e); + } + } + + public TbMsg copy(UUID newId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, long clusterPartition) { + return new TbMsg(newId, type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, clusterPartition); + } + +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleLifecycleComponent.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java similarity index 74% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleLifecycleComponent.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java index 0ab96ef666..2e367e9536 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleLifecycleComponent.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgDataType.java @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.rules; +package org.thingsboard.server.common.msg; /** - * @author Andrew Shvayka + * Created by ashvayka on 15.03.18. */ -public interface RuleLifecycleComponent { +public enum TbMsgDataType { - void resume(); - - void suspend(); - - void stop(); + // Do not change ordering. We use ordinal to save some bytes on serialization + JSON, TEXT, BINARY; } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java new file mode 100644 index 0000000000..e157aaa0d4 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2018 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.msg; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by ashvayka on 13.01.18. + */ +@Data +@NoArgsConstructor +public final class TbMsgMetaData implements Serializable { + + private final Map data = new ConcurrentHashMap<>(); + + public TbMsgMetaData(Map data) { + this.data.putAll(data); + } + + public String getValue(String key) { + return data.get(key); + } + + public void putValue(String key, String value) { + data.put(key, value); + } + + public Map values() { + return new HashMap<>(data); + } + + public TbMsgMetaData copy() { + return new TbMsgMetaData(new ConcurrentHashMap<>(data)); + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ClusterEventMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ClusterEventMsg.java index 67f4de7066..7d157f66a9 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ClusterEventMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ClusterEventMsg.java @@ -16,14 +16,20 @@ package org.thingsboard.server.common.msg.cluster; import lombok.Data; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; /** * @author Andrew Shvayka */ @Data -public final class ClusterEventMsg { +public final class ClusterEventMsg implements TbActorMsg { private final ServerAddress serverAddress; private final boolean added; + @Override + public MsgType getMsgType() { + return MsgType.CLUSTER_EVENT_MSG; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/SendToClusterMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/SendToClusterMsg.java new file mode 100644 index 0000000000..d3bc8e955d --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/SendToClusterMsg.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2018 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.msg.cluster; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; + +@Data +public class SendToClusterMsg implements TbActorMsg { + + private TbActorMsg msg; + private EntityId entityId; + + public SendToClusterMsg(EntityId entityId, TbActorMsg msg) { + this.entityId = entityId; + this.msg = msg; + } + + + @Override + public MsgType getMsgType() { + return MsgType.SEND_TO_CLUSTER_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java index 1dc33c038f..877689d0b9 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/cluster/ToAllNodesMsg.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.common.msg.cluster; +import org.thingsboard.server.common.msg.TbActorMsg; + import java.io.Serializable; /** * @author Andrew Shvayka */ -public interface ToAllNodesMsg extends Serializable { +public interface ToAllNodesMsg extends Serializable, TbActorMsg { } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java similarity index 84% rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceSessionActorMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java index f3e0c0fee4..6bb5c9911d 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceSessionActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ActorSystemToDeviceSessionActorMsg.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.msg.core; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.session.ToDeviceMsg; @@ -23,7 +24,7 @@ import java.io.Serializable; /** * @author Andrew Shvayka */ -public interface ToDeviceSessionActorMsg extends SessionAwareMsg, Serializable { +public interface ActorSystemToDeviceSessionActorMsg extends SessionAwareMsg, Serializable, TbActorMsg { ToDeviceMsg getMsg(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java index 6894ac54eb..cadaf3ce56 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesSubscribeMsg.java @@ -16,14 +16,14 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ public class AttributesSubscribeMsg implements FromDeviceMsg { @Override - public MsgType getMsgType() { - return MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java index e3fcd6fc79..c98ad2ae26 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUnsubscribeMsg.java @@ -16,14 +16,15 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ public class AttributesUnsubscribeMsg implements FromDeviceMsg { @Override - public MsgType getMsgType() { - return MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java index 13294891ed..1f0f9ddc99 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateNotification.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.msg.core; import lombok.ToString; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; @ToString @@ -36,9 +37,8 @@ public class AttributesUpdateNotification implements ToDeviceMsg { return true; } - @Override - public MsgType getMsgType() { - return MsgType.ATTRIBUTES_UPDATE_NOTIFICATION; + public SessionMsgType getSessionMsgType() { + return SessionMsgType.ATTRIBUTES_UPDATE_NOTIFICATION; } public AttributesKVMsg getData() { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/UpdateAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java similarity index 93% rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/UpdateAttributesRequest.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java index a2989624dd..d4cb4b1ae6 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/UpdateAttributesRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/AttributesUpdateRequest.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; -public interface UpdateAttributesRequest extends FromDeviceRequestMsg { +public interface AttributesUpdateRequest extends FromDeviceRequestMsg { Set getAttributes(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicToDeviceSessionActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java similarity index 71% rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicToDeviceSessionActorMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java index 6a70891bc7..cc298915c8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicToDeviceSessionActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicActorSystemToDeviceSessionActorMsg.java @@ -16,14 +16,15 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; -public class BasicToDeviceSessionActorMsg implements ToDeviceSessionActorMsg { +public class BasicActorSystemToDeviceSessionActorMsg implements ActorSystemToDeviceSessionActorMsg { private final ToDeviceMsg msg; private final SessionId sessionId; - public BasicToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) { + public BasicActorSystemToDeviceSessionActorMsg(ToDeviceMsg msg, SessionId sessionId) { super(); this.msg = msg; this.sessionId = sessionId; @@ -41,7 +42,11 @@ public class BasicToDeviceSessionActorMsg implements ToDeviceSessionActorMsg { @Override public String toString() { - return "BasicToSessionResponseMsg [msg=" + msg + ", sessionId=" + sessionId + "]"; + return "BasicActorSystemToDeviceSessionActorMsg [msg=" + msg + ", sessionId=" + sessionId + "]"; } + @Override + public MsgType getMsgType() { + return MsgType.ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicUpdateAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java similarity index 76% rename from common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicUpdateAttributesRequest.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java index 994fe5e5d0..76efca0f07 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicUpdateAttributesRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicAttributesUpdateRequest.java @@ -20,19 +20,19 @@ import java.util.LinkedHashSet; import java.util.Set; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; -public class BasicUpdateAttributesRequest extends BasicRequest implements UpdateAttributesRequest { +public class BasicAttributesUpdateRequest extends BasicRequest implements AttributesUpdateRequest { private static final long serialVersionUID = 1L; private final Set data; - public BasicUpdateAttributesRequest() { + public BasicAttributesUpdateRequest() { this(DEFAULT_REQUEST_ID); } - public BasicUpdateAttributesRequest(Integer requestId) { + public BasicAttributesUpdateRequest(Integer requestId) { super(requestId); this.data = new LinkedHashSet<>(); } @@ -46,8 +46,8 @@ public class BasicUpdateAttributesRequest extends BasicRequest implements Update } @Override - public MsgType getMsgType() { - return MsgType.POST_ATTRIBUTES_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.POST_ATTRIBUTES_REQUEST; } @Override @@ -57,7 +57,7 @@ public class BasicUpdateAttributesRequest extends BasicRequest implements Update @Override public String toString() { - return "BasicUpdateAttributesRequest [data=" + data + "]"; + return "BasicAttributesUpdateRequest [data=" + data + "]"; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java index ef772c3f17..3ee04ba216 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicCommandAckResponse.java @@ -15,26 +15,27 @@ */ package org.thingsboard.server.common.msg.core; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; public class BasicCommandAckResponse extends BasicResponseMsg implements StatusCodeResponse { private static final long serialVersionUID = 1L; - public static BasicCommandAckResponse onSuccess(MsgType requestMsgType, Integer requestId) { + public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) { return BasicCommandAckResponse.onSuccess(requestMsgType, requestId, 200); } - public static BasicCommandAckResponse onSuccess(MsgType requestMsgType, Integer requestId, Integer code) { + public static BasicCommandAckResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) { return new BasicCommandAckResponse(requestMsgType, requestId, true, null, code); } - public static BasicCommandAckResponse onError(MsgType requestMsgType, Integer requestId, Exception error) { + public static BasicCommandAckResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) { return new BasicCommandAckResponse(requestMsgType, requestId, false, error, null); } - private BasicCommandAckResponse(MsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { - super(requestMsgType, requestId, MsgType.TO_DEVICE_RPC_RESPONSE_ACK, success, error, code); + private BasicCommandAckResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { + super(requestMsgType, requestId, SessionMsgType.TO_DEVICE_RPC_RESPONSE_ACK, success, error, code); } @Override diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java index b431c14d28..e0f6d7ecc4 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesRequest.java @@ -16,7 +16,8 @@ package org.thingsboard.server.common.msg.core; import lombok.ToString; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.Collections; import java.util.Optional; @@ -41,8 +42,8 @@ public class BasicGetAttributesRequest extends BasicRequest implements GetAttrib } @Override - public MsgType getMsgType() { - return MsgType.GET_ATTRIBUTES_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.GET_ATTRIBUTES_REQUEST; } @Override diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java index 5072de25f7..e3eb15d807 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicGetAttributesResponse.java @@ -17,23 +17,24 @@ package org.thingsboard.server.common.msg.core; import lombok.ToString; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; @ToString public class BasicGetAttributesResponse extends BasicResponseMsg implements GetAttributesResponse { private static final long serialVersionUID = 1L; - public static BasicGetAttributesResponse onSuccess(MsgType requestMsgType, int requestId, AttributesKVMsg code) { + public static BasicGetAttributesResponse onSuccess(SessionMsgType requestMsgType, int requestId, AttributesKVMsg code) { return new BasicGetAttributesResponse(requestMsgType, requestId, true, null, code); } - public static BasicGetAttributesResponse onError(MsgType requestMsgType, int requestId, Exception error) { + public static BasicGetAttributesResponse onError(SessionMsgType requestMsgType, int requestId, Exception error) { return new BasicGetAttributesResponse(requestMsgType, requestId, false, error, null); } - private BasicGetAttributesResponse(MsgType requestMsgType, int requestId, boolean success, Exception error, AttributesKVMsg code) { - super(requestMsgType, requestId, MsgType.GET_ATTRIBUTES_RESPONSE, success, error, code); + private BasicGetAttributesResponse(SessionMsgType requestMsgType, int requestId, boolean success, Exception error, AttributesKVMsg code) { + super(requestMsgType, requestId, SessionMsgType.GET_ATTRIBUTES_RESPONSE, success, error, code); } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java index caaa15cb1b..c61d8e5e03 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicResponseMsg.java @@ -18,32 +18,33 @@ package org.thingsboard.server.common.msg.core; import java.io.Serializable; import java.util.Optional; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; public class BasicResponseMsg implements ResponseMsg { private static final long serialVersionUID = 1L; - private final MsgType requestMsgType; + private final SessionMsgType requestMsgType; private final Integer requestId; - private final MsgType msgType; + private final SessionMsgType sessionMsgType; private final boolean success; private final T data; private final Exception error; - protected BasicResponseMsg(MsgType requestMsgType, Integer requestId, MsgType msgType, boolean success, Exception error, T data) { + protected BasicResponseMsg(SessionMsgType requestMsgType, Integer requestId, SessionMsgType sessionMsgType, boolean success, Exception error, T data) { super(); this.requestMsgType = requestMsgType; this.requestId = requestId; - this.msgType = msgType; + this.sessionMsgType = sessionMsgType; this.success = success; this.error = error; this.data = data; } @Override - public MsgType getRequestMsgType() { + public SessionMsgType getRequestMsgType() { return requestMsgType; } @@ -72,8 +73,7 @@ public class BasicResponseMsg implements ResponseMsg return "BasicResponseMsg [success=" + success + ", data=" + data + ", error=" + error + "]"; } - @Override - public MsgType getMsgType() { - return msgType; + public SessionMsgType getSessionMsgType() { + return sessionMsgType; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java index 22b525bab6..f21aa857bf 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicStatusCodeResponse.java @@ -16,26 +16,27 @@ package org.thingsboard.server.common.msg.core; import lombok.ToString; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; @ToString public class BasicStatusCodeResponse extends BasicResponseMsg implements StatusCodeResponse { private static final long serialVersionUID = 1L; - public static BasicStatusCodeResponse onSuccess(MsgType requestMsgType, Integer requestId) { + public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId) { return BasicStatusCodeResponse.onSuccess(requestMsgType, requestId, 0); } - public static BasicStatusCodeResponse onSuccess(MsgType requestMsgType, Integer requestId, Integer code) { + public static BasicStatusCodeResponse onSuccess(SessionMsgType requestMsgType, Integer requestId, Integer code) { return new BasicStatusCodeResponse(requestMsgType, requestId, true, null, code); } - public static BasicStatusCodeResponse onError(MsgType requestMsgType, Integer requestId, Exception error) { + public static BasicStatusCodeResponse onError(SessionMsgType requestMsgType, Integer requestId, Exception error) { return new BasicStatusCodeResponse(requestMsgType, requestId, false, error, null); } - private BasicStatusCodeResponse(MsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { - super(requestMsgType, requestId, MsgType.STATUS_CODE_RESPONSE, success, error, code); + private BasicStatusCodeResponse(SessionMsgType requestMsgType, Integer requestId, boolean success, Exception error, Integer code) { + super(requestMsgType, requestId, SessionMsgType.STATUS_CODE_RESPONSE, success, error, code); } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java index 1d96a65e29..60faeb1829 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/BasicTelemetryUploadRequest.java @@ -21,7 +21,8 @@ import java.util.List; import java.util.Map; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; public class BasicTelemetryUploadRequest extends BasicRequest implements TelemetryUploadRequest { @@ -48,8 +49,8 @@ public class BasicTelemetryUploadRequest extends BasicRequest implements Telemet } @Override - public MsgType getMsgType() { - return MsgType.POST_TELEMETRY_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.POST_TELEMETRY_REQUEST; } @Override diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java index 2eb0959eec..3e70460dcb 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ResponseMsg.java @@ -18,12 +18,12 @@ package org.thingsboard.server.common.msg.core; import java.io.Serializable; import java.util.Optional; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; public interface ResponseMsg extends ToDeviceMsg { - MsgType getRequestMsgType(); + SessionMsgType getRequestMsgType(); Integer getRequestId(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java index d4fc1d7e07..f8f24e8d20 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcSubscribeMsg.java @@ -16,14 +16,15 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ public class RpcSubscribeMsg implements FromDeviceMsg { @Override - public MsgType getMsgType() { - return MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java index b1532d7b3b..23eb238d79 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RpcUnsubscribeMsg.java @@ -16,14 +16,15 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ public class RpcUnsubscribeMsg implements FromDeviceMsg { @Override - public MsgType getMsgType() { - return MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java index bc8ceb4dbc..dcfde0fb52 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineError.java @@ -21,7 +21,7 @@ package org.thingsboard.server.common.msg.core; public enum RuleEngineError { - NO_RULES, NO_ACTIVE_RULES, NO_FILTERS_MATCHED, NO_REQUEST_FROM_ACTIONS, NO_TWO_WAY_ACTIONS, NO_RESPONSE_FROM_ACTIONS, PLUGIN_TIMEOUT(true); + QUEUE_PUT_TIMEOUT(true), SERVER_ERROR(true), TIMEOUT; private final boolean critical; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java index 5cb3314e1d..e0ff23b379 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/RuleEngineErrorMsg.java @@ -16,7 +16,8 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; /** @@ -25,7 +26,7 @@ import org.thingsboard.server.common.msg.session.ToDeviceMsg; @Data public class RuleEngineErrorMsg implements ToDeviceMsg { - private final MsgType inMsgType; + private final SessionMsgType inSessionMsgType; private final RuleEngineError error; @Override @@ -33,27 +34,18 @@ public class RuleEngineErrorMsg implements ToDeviceMsg { return false; } - @Override - public MsgType getMsgType() { - return MsgType.RULE_ENGINE_ERROR; + public SessionMsgType getSessionMsgType() { + return SessionMsgType.RULE_ENGINE_ERROR; } public String getErrorMsg() { switch (error) { - case NO_RULES: - return "No rules configured!"; - case NO_ACTIVE_RULES: - return "No active rules!"; - case NO_FILTERS_MATCHED: - return "No rules that match current message!"; - case NO_REQUEST_FROM_ACTIONS: - return "Rule filters match, but no plugin message produced by rule action!"; - case NO_TWO_WAY_ACTIONS: - return "Rule filters match, but no rule with two-way action configured!"; - case NO_RESPONSE_FROM_ACTIONS: - return "Rule filters match, message processed by plugin, but no response produced by rule action!"; - case PLUGIN_TIMEOUT: - return "Timeout during processing of message by plugin!"; + case QUEUE_PUT_TIMEOUT: + return "Timeout during persistence of the message to the queue!"; + case SERVER_ERROR: + return "Error during processing of message by the server!"; + case TIMEOUT: + return "Timeout during processing of message by the server!"; default: throw new RuntimeException("Error " + error + " is not supported!"); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java index 738bc6e138..6d3ad5e8be 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseMsg.java @@ -16,14 +16,15 @@ package org.thingsboard.server.common.msg.core; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ public class SessionCloseMsg implements FromDeviceMsg { @Override - public MsgType getMsgType() { - return MsgType.SESSION_CLOSE; + public SessionMsgType getMsgType() { + return SessionMsgType.SESSION_CLOSE; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java index bf3c982922..bf36a9b3c3 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionCloseNotification.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.msg.core; import lombok.ToString; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; @ToString @@ -30,9 +31,8 @@ public class SessionCloseNotification implements ToDeviceMsg { return true; } - @Override - public MsgType getMsgType() { - return MsgType.SESSION_CLOSE; + public SessionMsgType getSessionMsgType() { + return SessionMsgType.SESSION_CLOSE; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java index f3a7bc7bae..28e319e525 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/SessionOpenMsg.java @@ -15,15 +15,21 @@ */ package org.thingsboard.server.common.msg.core; +import lombok.Data; +import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka */ +@Data public class SessionOpenMsg implements FromDeviceMsg { + @Override - public MsgType getMsgType() { - return MsgType.SESSION_OPEN; + public SessionMsgType getMsgType() { + return SessionMsgType.SESSION_OPEN; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java index c9eeb4ef1a..d1a1f96451 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcRequestMsg.java @@ -16,7 +16,8 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; /** @@ -29,9 +30,8 @@ public class ToDeviceRpcRequestMsg implements ToDeviceMsg { private final String method; private final String params; - @Override - public MsgType getMsgType() { - return MsgType.TO_DEVICE_RPC_REQUEST; + public SessionMsgType getSessionMsgType() { + return SessionMsgType.TO_DEVICE_RPC_REQUEST; } @Override diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java index ec739b379c..4fa3024947 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToDeviceRpcResponseMsg.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka @@ -29,7 +30,7 @@ public class ToDeviceRpcResponseMsg implements FromDeviceMsg { private final String data; @Override - public MsgType getMsgType() { - return MsgType.TO_DEVICE_RPC_RESPONSE; + public SessionMsgType getMsgType() { + return SessionMsgType.TO_DEVICE_RPC_RESPONSE; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java index 87708a759f..3823acac70 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcRequestMsg.java @@ -17,7 +17,7 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; /** * @author Andrew Shvayka @@ -30,7 +30,7 @@ public class ToServerRpcRequestMsg implements FromDeviceRequestMsg { private final String params; @Override - public MsgType getMsgType() { - return MsgType.TO_SERVER_RPC_REQUEST; + public SessionMsgType getMsgType() { + return SessionMsgType.TO_SERVER_RPC_REQUEST; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java index b9a43d13a9..82f44e9113 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/core/ToServerRpcResponseMsg.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.msg.core; import lombok.Data; import org.thingsboard.server.common.msg.session.FromDeviceMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.ToDeviceMsg; /** @@ -29,9 +30,8 @@ public class ToServerRpcResponseMsg implements ToDeviceMsg { private final int requestId; private final String data; - @Override - public MsgType getMsgType() { - return MsgType.TO_SERVER_RPC_RESPONSE; + public SessionMsgType getSessionMsgType() { + return SessionMsgType.TO_SERVER_RPC_RESPONSE; } @Override diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java similarity index 73% rename from common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicToDeviceActorMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java index 959da7c4b3..92c5105673 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicToDeviceActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/device/BasicDeviceToDeviceActorMsg.java @@ -20,15 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.SessionType; -import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; +import org.thingsboard.server.common.msg.session.TransportToDeviceSessionActorMsg; import java.util.Optional; @ToString -public class BasicToDeviceActorMsg implements ToDeviceActorMsg { +public class BasicDeviceToDeviceActorMsg implements DeviceToDeviceActorMsg { private static final long serialVersionUID = -1866795134993115408L; @@ -40,16 +41,16 @@ public class BasicToDeviceActorMsg implements ToDeviceActorMsg { private final ServerAddress serverAddress; private final FromDeviceMsg msg; - public BasicToDeviceActorMsg(ToDeviceActorMsg other, FromDeviceMsg msg) { + public BasicDeviceToDeviceActorMsg(DeviceToDeviceActorMsg other, FromDeviceMsg msg) { this(null, other.getTenantId(), other.getCustomerId(), other.getDeviceId(), other.getSessionId(), other.getSessionType(), msg); } - public BasicToDeviceActorMsg(ToDeviceActorSessionMsg msg, SessionType sessionType) { + public BasicDeviceToDeviceActorMsg(TransportToDeviceSessionActorMsg msg, SessionType sessionType) { this(null, msg.getTenantId(), msg.getCustomerId(), msg.getDeviceId(), msg.getSessionId(), sessionType, msg.getSessionMsg().getMsg()); } - private BasicToDeviceActorMsg(ServerAddress serverAddress, TenantId tenantId, CustomerId customerId, DeviceId deviceId, SessionId sessionId, SessionType sessionType, - FromDeviceMsg msg) { + private BasicDeviceToDeviceActorMsg(ServerAddress serverAddress, TenantId tenantId, CustomerId customerId, DeviceId deviceId, SessionId sessionId, SessionType sessionType, + FromDeviceMsg msg) { super(); this.serverAddress = serverAddress; this.tenantId = tenantId; @@ -95,7 +96,12 @@ public class BasicToDeviceActorMsg implements ToDeviceActorMsg { } @Override - public ToDeviceActorMsg toOtherAddress(ServerAddress otherAddress) { - return new BasicToDeviceActorMsg(otherAddress, tenantId, customerId, deviceId, sessionId, sessionType, msg); + public DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress) { + return new BasicDeviceToDeviceActorMsg(otherAddress, tenantId, customerId, deviceId, sessionId, sessionType, msg); + } + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_SESSION_TO_DEVICE_ACTOR_MSG; } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/device/ToDeviceActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java similarity index 84% rename from common/message/src/main/java/org/thingsboard/server/common/msg/device/ToDeviceActorMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java index 72e552d85b..fe8d9f2c21 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/device/ToDeviceActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/device/DeviceToDeviceActorMsg.java @@ -19,6 +19,7 @@ import java.io.Serializable; import java.util.Optional; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.CustomerAwareMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; @@ -26,7 +27,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.session.FromDeviceMsg; import org.thingsboard.server.common.msg.session.SessionType; -public interface ToDeviceActorMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, Serializable { +public interface DeviceToDeviceActorMsg extends TbActorMsg, DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, Serializable { SessionId getSessionId(); @@ -36,5 +37,5 @@ public interface ToDeviceActorMsg extends DeviceAwareMsg, CustomerAwareMsg, Tena FromDeviceMsg getPayload(); - ToDeviceActorMsg toOtherAddress(ServerAddress otherAddress); + DeviceToDeviceActorMsg toOtherAddress(ServerAddress otherAddress); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java index d48c3feb10..eec2de0154 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java @@ -15,14 +15,15 @@ */ package org.thingsboard.server.common.msg.plugin; -import lombok.Data; import lombok.Getter; import lombok.ToString; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; @@ -32,34 +33,26 @@ import java.util.Optional; * @author Andrew Shvayka */ @ToString -public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { +public class ComponentLifecycleMsg implements TbActorMsg, TenantAwareMsg, ToAllNodesMsg { @Getter private final TenantId tenantId; - private final PluginId pluginId; - private final RuleId ruleId; + @Getter + private final EntityId entityId; @Getter private final ComponentLifecycleEvent event; - public static ComponentLifecycleMsg forPlugin(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent event) { - return new ComponentLifecycleMsg(tenantId, pluginId, null, event); - } - - public static ComponentLifecycleMsg forRule(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent event) { - return new ComponentLifecycleMsg(tenantId, null, ruleId, event); - } - - private ComponentLifecycleMsg(TenantId tenantId, PluginId pluginId, RuleId ruleId, ComponentLifecycleEvent event) { + public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) { this.tenantId = tenantId; - this.pluginId = pluginId; - this.ruleId = ruleId; + this.entityId = entityId; this.event = event; } - public Optional getPluginId() { - return Optional.ofNullable(pluginId); + public Optional getRuleChainId() { + return entityId.getEntityType() == EntityType.RULE_CHAIN ? Optional.of((RuleChainId) entityId) : Optional.empty(); } - public Optional getRuleId() { - return Optional.ofNullable(ruleId); + @Override + public MsgType getMsgType() { + return MsgType.COMPONENT_LIFE_CYCLE_MSG; } } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java similarity index 84% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequest.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java index a303b69445..c5cb6bd2f8 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.common.msg.rpc; import lombok.Data; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; import java.io.Serializable; import java.util.UUID; @@ -29,7 +29,6 @@ import java.util.UUID; @Data public class ToDeviceRpcRequest implements Serializable { private final UUID id; - private final PluginApiCallSecurityContext securityCtx; private final TenantId tenantId; private final DeviceId deviceId; private final boolean oneway; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicToDeviceActorSessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java similarity index 75% rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicToDeviceActorSessionMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java index a318538ba8..0704e6e1bf 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicToDeviceActorSessionMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/BasicTransportToDeviceSessionActorMsg.java @@ -20,15 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; -public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg { +public class BasicTransportToDeviceSessionActorMsg implements TransportToDeviceSessionActorMsg { private final TenantId tenantId; private final CustomerId customerId; private final DeviceId deviceId; private final AdaptorToSessionActorMsg msg; - public BasicToDeviceActorSessionMsg(Device device, AdaptorToSessionActorMsg msg) { + public BasicTransportToDeviceSessionActorMsg(Device device, AdaptorToSessionActorMsg msg) { super(); this.tenantId = device.getTenantId(); this.customerId = device.getCustomerId(); @@ -36,13 +37,6 @@ public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg { this.msg = msg; } - public BasicToDeviceActorSessionMsg(ToDeviceActorSessionMsg deviceMsg) { - this.tenantId = deviceMsg.getTenantId(); - this.customerId = deviceMsg.getCustomerId(); - this.deviceId = deviceMsg.getDeviceId(); - this.msg = deviceMsg.getSessionMsg(); - } - @Override public DeviceId getDeviceId() { return deviceId; @@ -69,8 +63,12 @@ public class BasicToDeviceActorSessionMsg implements ToDeviceActorSessionMsg { @Override public String toString() { - return "BasicToDeviceActorSessionMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg + return "BasicTransportToDeviceSessionActorMsg [tenantId=" + tenantId + ", customerId=" + customerId + ", deviceId=" + deviceId + ", msg=" + msg + "]"; } + @Override + public MsgType getMsgType() { + return MsgType.TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java index 19d45a7095..71b605712f 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FromDeviceMsg.java @@ -19,6 +19,6 @@ import java.io.Serializable; public interface FromDeviceMsg extends Serializable { - MsgType getMsgType(); + SessionMsgType getMsgType(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java index 19ca219401..8082f72184 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionCtrlMsg.java @@ -15,8 +15,9 @@ */ package org.thingsboard.server.common.msg.session; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg; -public interface SessionCtrlMsg extends SessionAwareMsg { +public interface SessionCtrlMsg extends SessionAwareMsg, TbActorMsg { } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java similarity index 93% rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/MsgType.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java index ddc5149d14..c7109c9703 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/MsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.common.msg.session; -public enum MsgType { +public enum SessionMsgType { GET_ATTRIBUTES_REQUEST(true), POST_ATTRIBUTES_REQUEST(true), GET_ATTRIBUTES_RESPONSE, SUBSCRIBE_ATTRIBUTES_REQUEST, UNSUBSCRIBE_ATTRIBUTES_REQUEST, ATTRIBUTES_UPDATE_NOTIFICATION, @@ -32,11 +32,11 @@ public enum MsgType { private final boolean requiresRulesProcessing; - MsgType() { + SessionMsgType() { this(false); } - MsgType(boolean requiresRulesProcessing) { + SessionMsgType(boolean requiresRulesProcessing) { this.requiresRulesProcessing = requiresRulesProcessing; } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java index 31ec1fa7f9..705e86420b 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceMsg.java @@ -21,6 +21,6 @@ public interface ToDeviceMsg extends Serializable { boolean isSuccess(); - MsgType getMsgType(); + SessionMsgType getSessionMsgType(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceActorSessionMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java similarity index 83% rename from common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceActorSessionMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java index 9ecfe19604..deaef3d3e5 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ToDeviceActorSessionMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/TransportToDeviceSessionActorMsg.java @@ -15,12 +15,13 @@ */ package org.thingsboard.server.common.msg.session; +import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.aware.CustomerAwareMsg; import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; import org.thingsboard.server.common.msg.aware.SessionAwareMsg; import org.thingsboard.server.common.msg.aware.TenantAwareMsg; -public interface ToDeviceActorSessionMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg { +public interface TransportToDeviceSessionActorMsg extends DeviceAwareMsg, CustomerAwareMsg, TenantAwareMsg, SessionAwareMsg, TbActorMsg { AdaptorToSessionActorMsg getSessionMsg(); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java index a24bdcde90..49fad13236 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/ctrl/SessionCloseMsg.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.msg.session.ctrl; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.session.SessionCtrlMsg; public class SessionCloseMsg implements SessionCtrlMsg { @@ -60,4 +61,8 @@ public class SessionCloseMsg implements SessionCtrlMsg { return timeout; } + @Override + public MsgType getMsgType() { + return MsgType.SESSION_CTRL_MSG; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java new file mode 100644 index 0000000000..0792b63c28 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/system/ServiceToRuleEngineMsg.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2018 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.msg.system; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; + +/** + * Created by ashvayka on 15.03.18. + */ +@Data +public final class ServiceToRuleEngineMsg implements TbActorMsg { + + private final TenantId tenantId; + private final TbMsg tbMsg; + + @Override + public MsgType getMsgType() { + return MsgType.SERVICE_TO_RULE_ENGINE_MSG; + } +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutIntMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorClientSideRpcTimeoutMsg.java similarity index 64% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutIntMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorClientSideRpcTimeoutMsg.java index e25d5d7dd2..f5cdfd1c26 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutIntMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorClientSideRpcTimeoutMsg.java @@ -13,15 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.common.msg.timeout; + +import org.thingsboard.server.common.msg.MsgType; /** * @author Andrew Shvayka */ -public final class TimeoutIntMsg extends TimeoutMsg { +public final class DeviceActorClientSideRpcTimeoutMsg extends TimeoutMsg { - public TimeoutIntMsg(Integer id, long timeout) { + public DeviceActorClientSideRpcTimeoutMsg(Integer id, long timeout) { super(id, timeout); } + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java new file mode 100644 index 0000000000..0277983d8b --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorQueueTimeoutMsg.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2018 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.msg.timeout; + +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.timeout.TimeoutMsg; + +import java.util.UUID; + +/** + * @author Andrew Shvayka + */ +public final class DeviceActorQueueTimeoutMsg extends TimeoutMsg { + + public DeviceActorQueueTimeoutMsg(UUID id, long timeout) { + super(id, timeout); + } + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_ACTOR_QUEUE_TIMEOUT_MSG; + } +} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRpcMsgHandler.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorServerSideRpcTimeoutMsg.java similarity index 62% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRpcMsgHandler.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorServerSideRpcTimeoutMsg.java index 949fd02031..cd86892b7f 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRpcMsgHandler.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorServerSideRpcTimeoutMsg.java @@ -13,18 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.handlers; +package org.thingsboard.server.common.msg.timeout; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; +import org.thingsboard.server.common.msg.MsgType; /** * @author Andrew Shvayka */ -public class DefaultRpcMsgHandler implements RpcMsgHandler { +public final class DeviceActorServerSideRpcTimeoutMsg extends TimeoutMsg { + + public DeviceActorServerSideRpcTimeoutMsg(Integer id, long timeout) { + super(id, timeout); + } @Override - public void process(PluginContext ctx, RpcMsg msg) { - throw new RuntimeException("Not registered msg type: " + msg.getMsgClazz() + "!"); + public MsgType getMsgType() { + return MsgType.DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG; } } diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/TimeoutMsg.java similarity index 81% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutMsg.java rename to common/message/src/main/java/org/thingsboard/server/common/msg/timeout/TimeoutMsg.java index 03b2cd70ac..503188b006 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TimeoutMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/timeout/TimeoutMsg.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.common.msg.timeout; import lombok.Data; +import org.thingsboard.server.common.msg.TbActorMsg; /** * @author Andrew Shvayka */ @Data -public class TimeoutMsg { +public abstract class TimeoutMsg implements TbActorMsg { private final T id; private final long timeout; } diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java b/common/message/src/main/proto/tbmsg.proto similarity index 51% rename from application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java rename to common/message/src/main/proto/tbmsg.proto index 70a26e4430..60003dc420 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/SimpleRuleActorChain.java +++ b/common/message/src/main/proto/tbmsg.proto @@ -13,27 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +syntax = "proto3"; +package msgqueue; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +option java_package = "org.thingsboard.server.common.msg.gen"; +option java_outer_classname = "MsgProtos"; -public class SimpleRuleActorChain implements RuleActorChain { +message TbMsgMetaDataProto { + map data = 1; +} - private final List rules; +message TbMsgProto { + string id = 1; + string type = 2; + string entityType = 3; + int64 entityIdMSB = 4; + int64 entityIdLSB = 5; - public SimpleRuleActorChain(Set ruleSet) { - rules = new ArrayList<>(ruleSet); - rules.sort(RuleActorMetaData.RULE_ACTOR_MD_COMPARATOR); - } + int64 ruleChainIdMSB = 6; + int64 ruleChainIdLSB = 7; - public int size() { - return rules.size(); - } + int64 ruleNodeIdMSB = 8; + int64 ruleNodeIdLSB = 9; + int64 clusterPartition = 10; - public RuleActorMetaData getRuleActorMd(int index) { - return rules.get(index); - } + TbMsgMetaDataProto metaData = 11; -} + int32 dataType = 12; + string data = 13; + +} \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml index 0f921752d7..8644257dd4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT thingsboard org.thingsboard diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 18f2de6545..fe0ab72f31 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java index 93a6e9965f..080f874efb 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/TransportAdaptor.java @@ -16,7 +16,7 @@ package org.thingsboard.server.common.transport; import org.thingsboard.server.common.msg.session.AdaptorToSessionActorMsg; -import org.thingsboard.server.common.msg.session.MsgType; +import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.session.SessionActorToAdaptorMsg; import org.thingsboard.server.common.msg.session.SessionContext; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -25,7 +25,7 @@ import java.util.Optional; public interface TransportAdaptor { - AdaptorToSessionActorMsg convertToActorMsg(C ctx, MsgType type, T inbound) throws AdaptorException; + AdaptorToSessionActorMsg convertToActorMsg(C ctx, SessionMsgType type, T inbound) throws AdaptorException; Optional convertToAdaptorMsg(C ctx, SessionActorToAdaptorMsg msg) throws AdaptorException; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 390266a614..e7c173485c 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -36,9 +36,16 @@ public class JsonConverter { return convertToTelemetry(jsonObject, BasicRequest.DEFAULT_REQUEST_ID); } + public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long ts) throws JsonSyntaxException { + return convertToTelemetry(jsonObject, ts, BasicRequest.DEFAULT_REQUEST_ID); + } + public static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, int requestId) throws JsonSyntaxException { + return convertToTelemetry(jsonObject, System.currentTimeMillis(), requestId); + } + + private static TelemetryUploadRequest convertToTelemetry(JsonElement jsonObject, long systemTs, int requestId) throws JsonSyntaxException { BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); - long systemTs = System.currentTimeMillis(); if (jsonObject.isJsonObject()) { parseObject(request, systemTs, jsonObject); } else if (jsonObject.isJsonArray()) { @@ -118,13 +125,13 @@ public class JsonConverter { } } - public static UpdateAttributesRequest convertToAttributes(JsonElement element) { + public static AttributesUpdateRequest convertToAttributes(JsonElement element) { return convertToAttributes(element, BasicRequest.DEFAULT_REQUEST_ID); } - public static UpdateAttributesRequest convertToAttributes(JsonElement element, int requestId) { + public static AttributesUpdateRequest convertToAttributes(JsonElement element, int requestId) { if (element.isJsonObject()) { - BasicUpdateAttributesRequest request = new BasicUpdateAttributesRequest(requestId); + BasicAttributesUpdateRequest request = new BasicAttributesUpdateRequest(requestId); long ts = System.currentTimeMillis(); request.add(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); return request; diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java similarity index 73% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaService.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java index c1f045df8f..9d7bd1720c 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaService.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java @@ -15,33 +15,24 @@ */ package org.thingsboard.server.common.transport.quota; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -/** - * @author Vitaliy Paromskiy - * @version 1.0 - */ -@Service -@Slf4j -public class HostRequestsQuotaService implements QuotaService { +public class AbstractQuotaService implements QuotaService { - private final HostRequestIntervalRegistry requestRegistry; - private final HostRequestLimitPolicy requestsPolicy; + private final KeyBasedIntervalRegistry requestRegistry; + private final RequestLimitPolicy requestsPolicy; private final IntervalRegistryCleaner registryCleaner; private final IntervalRegistryLogger registryLogger; private final boolean enabled; - public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, + public AbstractQuotaService(KeyBasedIntervalRegistry requestRegistry, RequestLimitPolicy requestsPolicy, IntervalRegistryCleaner registryCleaner, IntervalRegistryLogger registryLogger, - @Value("${quota.host.enabled}") boolean enabled) { + boolean enabled) { this.requestRegistry = requestRegistry; this.requestsPolicy = requestsPolicy; this.registryCleaner = registryCleaner; diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleException.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java similarity index 68% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleException.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java index b1b82be67c..0ff1230b1c 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleException.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.rules; +package org.thingsboard.server.common.transport.quota; -public class RuleException extends Exception { - private static final long serialVersionUID = 1L; +public abstract class RequestLimitPolicy { - public RuleException(String msg) { - super(msg); - } + private final long limit; - public RuleException(String msg, Exception e) { - super(msg, e); + public RequestLimitPolicy(long limit) { + this.limit = limit; } + public boolean isValid(long currentValue) { + return currentValue <= limit; + } } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java new file mode 100644 index 0000000000..ea75f5dc09 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryCleaner.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2018 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.transport.quota.host; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; + +@Component +public class HostIntervalRegistryCleaner extends IntervalRegistryCleaner { + + public HostIntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, + @Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { + super(intervalRegistry, cleanPeriodMs); + } +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java new file mode 100644 index 0000000000..65767f1648 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostIntervalRegistryLogger.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2018 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.transport.quota.host; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Component +@Slf4j +public class HostIntervalRegistryLogger extends IntervalRegistryLogger { + + private final long logIntervalMin; + + public HostIntervalRegistryLogger(@Value("${quota.host.log.topSize}") int topSize, + @Value("${quota.host.log.intervalMin}") long logIntervalMin, + HostRequestIntervalRegistry intervalRegistry) { + super(topSize, logIntervalMin, intervalRegistry); + this.logIntervalMin = logIntervalMin; + } + + protected void log(Map top, int uniqHosts, long requestsCount) { + long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); + StringBuilder builder = new StringBuilder("Quota Statistic : "); + builder.append("uniqHosts : ").append(uniqHosts).append("; "); + builder.append("requestsCount : ").append(requestsCount).append("; "); + builder.append("RPS : ").append(rps).append(" "); + builder.append("top -> "); + for (Map.Entry host : top.entrySet()) { + builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); + } + + log.info(builder.toString()); + } +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java new file mode 100644 index 0000000000..9b3b4614e4 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestIntervalRegistry.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2018 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.transport.quota.host; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; + +/** + * @author Vitaliy Paromskiy + * @version 1.0 + */ +@Component +@Slf4j +public class HostRequestIntervalRegistry extends KeyBasedIntervalRegistry { + + public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, + @Value("${quota.host.ttlMs}") long ttlMs, + @Value("${quota.host.whitelist}") String whiteList, + @Value("${quota.host.blacklist}") String blackList) { + super(intervalDurationMs, ttlMs, whiteList, blackList, "host"); + } +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicy.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java similarity index 78% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicy.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java index cf1c4e85ae..eeef9247e6 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicy.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java @@ -13,26 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.transport.quota; +package org.thingsboard.server.common.transport.quota.host; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; /** * @author Vitaliy Paromskiy * @version 1.0 */ @Component -public class HostRequestLimitPolicy { - - private final long limit; +public class HostRequestLimitPolicy extends RequestLimitPolicy { public HostRequestLimitPolicy(@Value("${quota.host.limit}") long limit) { - this.limit = limit; - } - - public boolean isValid(long currentValue) { - return currentValue <= limit; + super(limit); } } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java new file mode 100644 index 0000000000..69342b538b --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestsQuotaService.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2018 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.transport.quota.host; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.transport.quota.AbstractQuotaService; + +/** + * @author Vitaliy Paromskiy + * @version 1.0 + */ +@Service +@Slf4j +public class HostRequestsQuotaService extends AbstractQuotaService { + + public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, + HostIntervalRegistryCleaner registryCleaner, HostIntervalRegistryLogger registryLogger, + @Value("${quota.host.enabled}") boolean enabled) { + super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); + } + +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java index a227d2abba..0c510ff815 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryCleaner.java @@ -16,10 +16,7 @@ package org.thingsboard.server.common.transport.quota.inmemory; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import javax.annotation.PreDestroy; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -28,15 +25,14 @@ import java.util.concurrent.TimeUnit; * @author Vitaliy Paromskiy * @version 1.0 */ -@Component @Slf4j -public class IntervalRegistryCleaner { +public abstract class IntervalRegistryCleaner { - private final HostRequestIntervalRegistry intervalRegistry; + private final KeyBasedIntervalRegistry intervalRegistry; private final long cleanPeriodMs; private ScheduledExecutorService executor; - public IntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, @Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { + public IntervalRegistryCleaner(KeyBasedIntervalRegistry intervalRegistry, long cleanPeriodMs) { this.intervalRegistry = intervalRegistry; this.cleanPeriodMs = cleanPeriodMs; } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java index 8b34a6becc..30399a140f 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLogger.java @@ -17,8 +17,6 @@ package org.thingsboard.server.common.transport.quota.inmemory; import com.google.common.collect.MinMaxPriorityQueue; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; import java.util.Comparator; import java.util.Map; @@ -32,17 +30,15 @@ import java.util.stream.Collectors; * @author Vitaliy Paromskiy * @version 1.0 */ -@Component @Slf4j -public class IntervalRegistryLogger { +public abstract class IntervalRegistryLogger { private final int topSize; - private final HostRequestIntervalRegistry intervalRegistry; + private final KeyBasedIntervalRegistry intervalRegistry; private final long logIntervalMin; private ScheduledExecutorService executor; - public IntervalRegistryLogger(@Value("${quota.log.topSize}") int topSize, @Value("${quota.log.intervalMin}") long logIntervalMin, - HostRequestIntervalRegistry intervalRegistry) { + public IntervalRegistryLogger(int topSize, long logIntervalMin, KeyBasedIntervalRegistry intervalRegistry) { this.topSize = topSize; this.logIntervalMin = logIntervalMin; this.intervalRegistry = intervalRegistry; @@ -79,17 +75,5 @@ public class IntervalRegistryLogger { return topQueue.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } - private void log(Map top, int uniqHosts, long requestsCount) { - long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); - StringBuilder builder = new StringBuilder("Quota Statistic : "); - builder.append("uniqHosts : ").append(uniqHosts).append("; "); - builder.append("requestsCount : ").append(requestsCount).append("; "); - builder.append("RPS : ").append(rps).append(" "); - builder.append("top -> "); - for (Map.Entry host : top.entrySet()) { - builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); - } - - log.info(builder.toString()); - } + protected abstract void log(Map top, int uniqHosts, long requestsCount); } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java similarity index 71% rename from common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java index 3782ed22ed..0b0fef85e5 100644 --- a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java @@ -18,22 +18,14 @@ package org.thingsboard.server.common.transport.quota.inmemory; import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -/** - * @author Vitaliy Paromskiy - * @version 1.0 - */ -@Component @Slf4j -public class HostRequestIntervalRegistry { +public abstract class KeyBasedIntervalRegistry { private final Map hostCounts = new ConcurrentHashMap<>(); private final long intervalDurationMs; @@ -41,23 +33,20 @@ public class HostRequestIntervalRegistry { private final Set whiteList; private final Set blackList; - public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, - @Value("${quota.host.ttlMs}") long ttlMs, - @Value("${quota.host.whitelist}") String whiteList, - @Value("${quota.host.blacklist}") String blackList) { + public KeyBasedIntervalRegistry(long intervalDurationMs, long ttlMs, String whiteList, String blackList, String name) { this.intervalDurationMs = intervalDurationMs; this.ttlMs = ttlMs; this.whiteList = Sets.newHashSet(StringUtils.split(whiteList, ',')); this.blackList = Sets.newHashSet(StringUtils.split(blackList, ',')); + validate(name); } - @PostConstruct - public void init() { + private void validate(String name) { if (ttlMs < intervalDurationMs) { - log.warn("TTL for IntervalRegistry [{}] smaller than interval duration [{}]", ttlMs, intervalDurationMs); + log.warn("TTL for {} IntervalRegistry [{}] smaller than interval duration [{}]", name, ttlMs, intervalDurationMs); } - log.info("Start Host Quota Service with whitelist {}", whiteList); - log.info("Start Host Quota Service with blacklist {}", blackList); + log.info("Start {} KeyBasedIntervalRegistry with whitelist {}", name, whiteList); + log.info("Start {} KeyBasedIntervalRegistry with blacklist {}", name, blackList); } public long tick(String clientHostId) { diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java similarity index 51% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java index c0d0705c4d..c48117069a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleContextAwareMsgProcessor.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryCleaner.java @@ -13,21 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.common.transport.quota.tenant; -import org.thingsboard.server.actors.ActorSystemContext; -import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; -import org.thingsboard.server.common.data.id.RuleId; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; -import akka.event.LoggingAdapter; +@Component +public class TenantIntervalRegistryCleaner extends IntervalRegistryCleaner { -public class RuleContextAwareMsgProcessor extends AbstractContextAwareMsgProcessor { - - private final RuleId ruleId; - - protected RuleContextAwareMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, RuleId ruleId) { - super(systemContext, logger); - this.ruleId = ruleId; + public TenantIntervalRegistryCleaner(TenantMsgsIntervalRegistry intervalRegistry, + @Value("${quota.rule.tenant.cleanPeriodMs}") long cleanPeriodMs) { + super(intervalRegistry, cleanPeriodMs); } - } diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java new file mode 100644 index 0000000000..c56f457423 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantIntervalRegistryLogger.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2018 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.transport.quota.tenant; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class TenantIntervalRegistryLogger extends IntervalRegistryLogger { + + private final long logIntervalMin; + + public TenantIntervalRegistryLogger(@Value("${quota.rule.tenant.log.topSize}") int topSize, + @Value("${quota.rule.tenant.log.intervalMin}") long logIntervalMin, + TenantMsgsIntervalRegistry intervalRegistry) { + super(topSize, logIntervalMin, intervalRegistry); + this.logIntervalMin = logIntervalMin; + } + + protected void log(Map top, int uniqHosts, long requestsCount) { + long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); + StringBuilder builder = new StringBuilder("Tenant Quota Statistic : "); + builder.append("uniqTenants : ").append(uniqHosts).append("; "); + builder.append("requestsCount : ").append(requestsCount).append("; "); + builder.append("RPS : ").append(rps).append(" "); + builder.append("top -> "); + for (Map.Entry host : top.entrySet()) { + builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); + } + + log.info(builder.toString()); + } +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java new file mode 100644 index 0000000000..6e8402c701 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantMsgsIntervalRegistry.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2018 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.transport.quota.tenant; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; + +@Component +public class TenantMsgsIntervalRegistry extends KeyBasedIntervalRegistry { + + public TenantMsgsIntervalRegistry(@Value("${quota.rule.tenant.intervalMs}") long intervalDurationMs, + @Value("${quota.rule.tenant.ttlMs}") long ttlMs, + @Value("${quota.rule.tenant.whitelist}") String whiteList, + @Value("${quota.rule.tenant.blacklist}") String blackList) { + super(intervalDurationMs, ttlMs, whiteList, blackList, "Rule Tenant"); + } +} diff --git a/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java new file mode 100644 index 0000000000..a68860a2e0 --- /dev/null +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantQuotaService.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2018 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.transport.quota.tenant; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.AbstractQuotaService; + +@Component +public class TenantQuotaService extends AbstractQuotaService { + + public TenantQuotaService(TenantMsgsIntervalRegistry requestRegistry, TenantRequestLimitPolicy requestsPolicy, + TenantIntervalRegistryCleaner registryCleaner, TenantIntervalRegistryLogger registryLogger, + @Value("${quota.rule.tenant.enabled}") boolean enabled) { + super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); + } +} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java similarity index 59% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java rename to common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java index 4458e5217c..cc32c81ad7 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleTerminationMsg.java +++ b/common/transport/src/main/java/org/thingsboard/server/common/transport/quota/tenant/TenantRequestLimitPolicy.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.common.transport.quota.tenant; -import org.thingsboard.server.actors.shared.ActorTerminationMsg; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.RuleId; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; -/** - * @author Andrew Shvayka - */ -public class RuleTerminationMsg extends ActorTerminationMsg { +@Component +public class TenantRequestLimitPolicy extends RequestLimitPolicy { - public RuleTerminationMsg(RuleId id) { - super(id); + public TenantRequestLimitPolicy(@Value("${quota.rule.tenant.limit}") long limit) { + super(limit); } } diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java index 174d18235d..07e03ef0ac 100644 --- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java +++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicyTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.transport.quota; import org.junit.Test; +import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java index 547f0cf0e4..20f8a55d0d 100644 --- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java +++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaServiceTest.java @@ -17,9 +17,7 @@ package org.thingsboard.server.common.transport.quota; import org.junit.Before; import org.junit.Test; -import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; -import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; -import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; +import org.thingsboard.server.common.transport.quota.host.*; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -35,8 +33,8 @@ public class HostRequestsQuotaServiceTest { private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); private HostRequestLimitPolicy requestsPolicy = mock(HostRequestLimitPolicy.class); - private IntervalRegistryCleaner registryCleaner = mock(IntervalRegistryCleaner.class); - private IntervalRegistryLogger registryLogger = mock(IntervalRegistryLogger.class); + private HostIntervalRegistryCleaner registryCleaner = mock(HostIntervalRegistryCleaner.class); + private HostIntervalRegistryLogger registryLogger = mock(HostIntervalRegistryLogger.class); @Before public void init() { diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java index 78b82eec08..b49dd00bc9 100644 --- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java +++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistryTest.java @@ -15,11 +15,9 @@ */ package org.thingsboard.server.common.transport.quota.inmemory; -import com.google.common.collect.Sets; import org.junit.Before; import org.junit.Test; - -import java.util.Collections; +import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; import static org.junit.Assert.assertEquals; diff --git a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java index c9139aeeda..6e51420cd5 100644 --- a/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java +++ b/common/transport/src/test/java/org/thingsboard/server/common/transport/quota/inmemory/IntervalRegistryLoggerTest.java @@ -18,6 +18,8 @@ package org.thingsboard.server.common.transport.quota.inmemory; import com.google.common.collect.ImmutableMap; import org.junit.Before; import org.junit.Test; +import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger; +import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; import java.util.Collections; import java.util.Map; @@ -37,7 +39,7 @@ public class IntervalRegistryLoggerTest { @Before public void init() { - logger = new IntervalRegistryLogger(3, 10, requestRegistry); + logger = new HostIntervalRegistryLogger(3, 10, requestRegistry); } @Test diff --git a/dao/pom.xml b/dao/pom.xml index 3eb267ea27..b9a180a248 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,10 +20,9 @@ 4.0.0 org.thingsboard - 1.4.1-SNAPSHOT + 2.0.0-SNAPSHOT thingsboard - org.thingsboard dao jar @@ -39,7 +38,11 @@ org.thingsboard.common data - + + + org.thingsboard.common + message + org.slf4j slf4j-api @@ -142,25 +145,17 @@ test - org.apache.curator - curator-x-discovery - - - com.hazelcast - hazelcast-zookeeper + com.google.protobuf + protobuf-java - com.hazelcast - hazelcast + org.apache.curator + curator-x-discovery com.github.ben-manes.caffeine caffeine - - com.hazelcast - hazelcast-spring - org.springframework.boot spring-boot-autoconfigure diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index fc434bf894..c7bcfbcd33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,7 +18,11 @@ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.model.ToData; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.UUID; public abstract class DaoUtil { diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 4da7df22d9..6638818207 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -15,8 +15,15 @@ */ package org.thingsboard.server.dao.alarm; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.alarm.*; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmId; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmQuery; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; @@ -30,7 +37,7 @@ public interface AlarmService { ListenableFuture ackAlarm(AlarmId alarmId, long ackTs); - ListenableFuture clearAlarm(AlarmId alarmId, long ackTs); + ListenableFuture clearAlarm(AlarmId alarmId, JsonNode details, long ackTs); ListenableFuture findAlarmByIdAsync(AlarmId alarmId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index e669ae226b..23fe85a802 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -16,8 +16,8 @@ package org.thingsboard.server.dao.alarm; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -25,19 +25,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.alarm.*; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmId; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmQuery; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.common.data.relation.EntityRelationsQuery; -import org.thingsboard.server.common.data.relation.EntitySearchDirection; -import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantDao; @@ -187,7 +193,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public ListenableFuture clearAlarm(AlarmId alarmId, long clearTime) { + public ListenableFuture clearAlarm(AlarmId alarmId, JsonNode details, long clearTime) { return getAndUpdate(alarmId, new Function() { @Nullable @Override @@ -199,6 +205,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK; alarm.setStatus(newStatus); alarm.setClearTs(clearTime); + if (details != null) { + alarm.setDetails(details); + } alarmDao.save(alarm); updateRelations(alarm, oldStatus, newStatus); return true; @@ -218,15 +227,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ public ListenableFuture findAlarmInfoByIdAsync(AlarmId alarmId) { log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId); - return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()), - (AsyncFunction) alarm1 -> { - AlarmInfo alarmInfo = new AlarmInfo(alarm1); + return Futures.transformAsync(alarmDao.findAlarmByIdAsync(alarmId.getId()), + a -> { + AlarmInfo alarmInfo = new AlarmInfo(a); return Futures.transform( - entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function) - originatorName -> { - alarmInfo.setOriginatorName(originatorName); - return alarmInfo; - } + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> { + alarmInfo.setOriginatorName(originatorName); + return alarmInfo; + } ); }); } @@ -235,18 +243,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ public ListenableFuture> findAlarms(AlarmQuery query) { ListenableFuture> alarms = alarmDao.findAlarms(query); if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { - alarms = Futures.transform(alarms, (AsyncFunction, List>) input -> { + alarms = Futures.transformAsync(alarms, input -> { List> alarmFutures = new ArrayList<>(input.size()); for (AlarmInfo alarmInfo : input) { alarmFutures.add(Futures.transform( - entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function) - originatorName -> { - if (originatorName == null) { - originatorName = "Deleted"; - } - alarmInfo.setOriginatorName(originatorName); - return alarmInfo; - } + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), originatorName -> { + if (originatorName == null) { + originatorName = "Deleted"; + } + alarmInfo.setOriginatorName(originatorName); + return alarmInfo; + } )); } return Futures.successfulAsList(alarmFutures); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java index 1233c7f753..646c0556ae 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java @@ -17,8 +17,6 @@ package org.thingsboard.server.dao.alarm; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; -import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -45,7 +43,12 @@ import java.util.UUID; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_BY_ID_VIEW_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY; @Component @Slf4j @@ -102,12 +105,12 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); - return Futures.transform(relations, (AsyncFunction, List>) input -> { + return Futures.transformAsync(relations, input -> { List> alarmFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { alarmFutures.add(Futures.transform( findAlarmByIdAsync(relation.getTo().getId()), - (Function) AlarmInfo::new)); + AlarmInfo::new)); } return Futures.successfulAsList(alarmFutures); }); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index dcd9523975..1eafb07442 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.asset; import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -45,12 +44,19 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; -import static org.thingsboard.server.dao.service.Validator.*; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateIds; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validateString; @Service @Slf4j @@ -194,10 +200,10 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @Override public ListenableFuture> findAssetsByQuery(AssetSearchQuery query) { ListenableFuture> relations = relationService.findByQuery(query.toEntitySearchQuery()); - ListenableFuture> assets = Futures.transform(relations, (AsyncFunction, List>) relations1 -> { + ListenableFuture> assets = Futures.transformAsync(relations, r -> { EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); List> futures = new ArrayList<>(); - for (EntityRelation relation : relations1) { + for (EntityRelation relation : r) { EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); if (entityId.getEntityType() == EntityType.ASSET) { futures.add(findAssetByIdAsync(new AssetId(entityId.getId()))); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java index 64ec718fdd..80a6f43f75 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java @@ -36,10 +36,30 @@ import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; import org.thingsboard.server.dao.util.NoSqlDao; import javax.annotation.Nullable; -import java.util.*; - -import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.in; +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_BY_TENANT_AND_NAME_VIEW_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Component @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java index 8ae9dc8ad4..a736a69c5e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.attributes; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Statement; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.google.common.base.Function; @@ -41,7 +45,12 @@ import java.util.stream.Collectors; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTES_KV_CF; +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.LAST_UPDATE_TS_COLUMN; /** * @author Andrew Shvayka diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java index 19651a09ab..d2e6f056e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java @@ -15,15 +15,16 @@ */ package org.thingsboard.server.dao.audit; -import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.*; +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.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 23fadeba0e..a30c1b453b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -34,10 +34,16 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.id.AuditLogId; +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.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.audit.sink.AuditLogSink; import org.thingsboard.server.dao.entity.EntityService; @@ -158,11 +164,20 @@ public class AuditLogServiceImpl implements AuditLogService { switch(actionType) { case ADDED: case UPDATED: - ObjectNode entityNode = objectMapper.valueToTree(entity); - if (entityId.getEntityType() == EntityType.DASHBOARD) { - entityNode.put("configuration", ""); + if (entity != null) { + ObjectNode entityNode = objectMapper.valueToTree(entity); + if (entityId.getEntityType() == EntityType.DASHBOARD) { + entityNode.put("configuration", ""); + } + actionData.set("entity", entityNode); + } + if (entityId.getEntityType() == EntityType.RULE_CHAIN) { + RuleChainMetaData ruleChainMetaData = extractParameter(RuleChainMetaData.class, additionalInfo); + if (ruleChainMetaData != null) { + ObjectNode ruleChainMetaDataNode = objectMapper.valueToTree(ruleChainMetaData); + actionData.set("metadata", ruleChainMetaDataNode); + } } - actionData.set("entity", entityNode); break; case DELETED: case ACTIVATED: diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java index fd02b5f880..764f4687d3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java @@ -48,13 +48,22 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneOffset; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.StringJoiner; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_BY_CUSTOMER_ID_CF; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_BY_TENANT_ID_CF; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_BY_USER_ID_CF; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_COLUMN_FAMILY_NAME; @Component @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java index 3706e50e41..b14d6b196d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java @@ -22,7 +22,11 @@ import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.*; +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.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java index cfb69f30ff..3be33f3c17 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.cache; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java index ca94822301..fd619765d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java @@ -16,10 +16,17 @@ package org.thingsboard.server.dao.cassandra; -import com.datastax.driver.core.*; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.HostDistance; +import com.datastax.driver.core.PoolingOptions; import com.datastax.driver.core.ProtocolOptions.Compression; +import com.datastax.driver.core.Session; +import com.datastax.driver.mapping.DefaultPropertyMapper; import com.datastax.driver.mapping.Mapper; +import com.datastax.driver.mapping.MappingConfiguration; import com.datastax.driver.mapping.MappingManager; +import com.datastax.driver.mapping.PropertyAccessStrategy; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -149,7 +156,13 @@ public abstract class AbstractCassandraCluster { } else { session = cluster.connect(); } - mappingManager = new MappingManager(session); +// For Cassandra Driver version 3.5.0 + DefaultPropertyMapper propertyMapper = new DefaultPropertyMapper(); + propertyMapper.setPropertyAccessStrategy(PropertyAccessStrategy.FIELDS); + MappingConfiguration configuration = MappingConfiguration.builder().withPropertyMapper(propertyMapper).build(); + mappingManager = new MappingManager(session, configuration); +// For Cassandra Driver version 3.0.0 +// mappingManager = new MappingManager(session); break; } catch (Exception e) { log.warn("Failed to initialize cassandra cluster due to {}. Will retry in {} ms", e.getMessage(), initRetryInterval); diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java index 9fea6186f5..598f98ac47 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CassandraCustomerDao.java @@ -33,7 +33,9 @@ import java.util.UUID; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_BY_TENANT_AND_TITLE_VIEW_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY; @Component @Slf4j @NoSqlDao diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java index 8091b2a1fe..13a27c2ef5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.dashboard; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -40,7 +39,9 @@ import java.util.List; import java.util.UUID; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY; @Component @Slf4j @@ -77,7 +78,7 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); - return Futures.transform(relations, (AsyncFunction, List>) input -> { + return Futures.transformAsync(relations, input -> { List> dashboardFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { dashboardFutures.add(findByIdAsync(relation.getTo().getId())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index 44d18acea9..c69364b8ce 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java @@ -26,8 +26,6 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; -import java.util.Set; - public interface DashboardService { Dashboard findDashboardById(DashboardId dashboardId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 6ce52861bb..e1f4e3c282 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -22,8 +22,10 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.*; -import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; @@ -36,8 +38,6 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.TimePaginatedRemover; @@ -45,11 +45,7 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; -import java.sql.Time; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutionException; import static org.thingsboard.server.dao.service.Validator.validateId; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java index 641c464cec..110f2071c7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java @@ -36,10 +36,30 @@ import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; import org.thingsboard.server.dao.util.NoSqlDao; import javax.annotation.Nullable; -import java.util.*; - -import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.in; +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_BY_TENANT_AND_NAME_VIEW_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Component @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 3a7c5e39fc..c5edd9e03c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import java.util.List; -import java.util.Optional; public interface DeviceService { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 9120619a74..3930e3a94f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.device; import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -28,7 +27,11 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; @@ -48,13 +51,20 @@ import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; -import static org.thingsboard.server.dao.service.Validator.*; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateIds; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validateString; @Service @Slf4j @@ -227,10 +237,10 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override public ListenableFuture> findDevicesByQuery(DeviceSearchQuery query) { ListenableFuture> relations = relationService.findByQuery(query.toEntitySearchQuery()); - ListenableFuture> devices = Futures.transform(relations, (AsyncFunction, List>) relations1 -> { + ListenableFuture> devices = Futures.transformAsync(relations, r -> { EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); List> futures = new ArrayList<>(); - for (EntityRelation relation : relations1) { + for (EntityRelation relation : r) { EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom(); if (entityId.getEntityType() == EntityType.DEVICE) { futures.add(findDeviceByIdAsync(new DeviceId(entityId.getId()))); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index 50723f3468..bc530ab536 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -20,8 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.relation.RelationService; -import java.util.concurrent.ExecutionException; - @Slf4j public abstract class AbstractEntityService { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index acc88eb833..1b67ca0486 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -29,8 +29,7 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; @@ -47,12 +46,6 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Autowired private DeviceService deviceService; - @Autowired - private RuleService ruleService; - - @Autowired - private PluginService pluginService; - @Autowired private TenantService tenantService; @@ -68,6 +61,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Autowired private AlarmService alarmService; + @Autowired + private RuleChainService ruleChainService; + @Override public void deleteEntityRelations(EntityId entityId) { super.deleteEntityRelations(entityId); @@ -85,12 +81,6 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case DEVICE: hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); break; - case RULE: - hasName = ruleService.findRuleByIdAsync(new RuleId(entityId.getId())); - break; - case PLUGIN: - hasName = pluginService.findPluginByIdAsync(new PluginId(entityId.getId())); - break; case TENANT: hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); break; @@ -106,6 +96,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case ALARM: hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId())); break; + case RULE_CHAIN: + hasName = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId())); + break; default: throw new IllegalStateException("Not Implemented!"); } 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 b8ba4a73fb..7dddec17ef 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.event; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -43,6 +44,12 @@ public class BaseEventService implements EventService { return eventDao.save(event); } + @Override + public ListenableFuture saveAsync(Event event) { + eventValidator.validate(event); + return eventDao.saveAsync(event); + } + @Override public Optional saveIfNotExists(Event event) { eventValidator.validate(event); @@ -82,6 +89,11 @@ public class BaseEventService implements EventService { return new TimePageData<>(events, pageLink); } + @Override + public List findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit) { + return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit); + } + private DataValidator eventValidator = new DataValidator() { @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java index 43d3fd50e7..7549e40108 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/CassandraBaseEventDao.java @@ -15,11 +15,13 @@ */ package org.thingsboard.server.dao.event; -import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.utils.UUIDs; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -38,6 +40,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.ExecutionException; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; @@ -62,6 +65,15 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao saveAsync(Event event) { log.debug("Save event [{}] ", event); if (event.getTenantId() == null) { log.trace("Save system event with predefined id {}", systemTenantId); @@ -73,7 +85,8 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao> optionalSave = saveAsync(new EventEntity(event), false); + return Futures.transform(optionalSave, opt -> opt.orElse(null)); } @Override @@ -134,7 +147,30 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) { + log.trace("Try to find latest events by tenant [{}], entity [{}], type [{}] and limit [{}]", tenantId, entityId, eventType, limit); + Select select = select().from(EVENT_BY_TYPE_AND_ID_VIEW_NAME); + Select.Where query = select.where(); + query.and(eq(ModelConstants.EVENT_TENANT_ID_PROPERTY, tenantId)); + query.and(eq(ModelConstants.EVENT_ENTITY_TYPE_PROPERTY, entityId.getEntityType())); + query.and(eq(ModelConstants.EVENT_ENTITY_ID_PROPERTY, entityId.getId())); + query.and(eq(ModelConstants.EVENT_TYPE_PROPERTY, eventType)); + query.limit(limit); + query.orderBy(QueryBuilder.desc(ModelConstants.EVENT_TYPE_PROPERTY), QueryBuilder.desc(ModelConstants.ID_PROPERTY)); + List entities = findListByStatement(query); + return DaoUtil.convertDataList(entities); + } + private Optional save(EventEntity entity, boolean ifNotExists) { + try { + return saveAsync(entity, ifNotExists).get(); + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException("Could not save EventEntity", e); + } + } + + private ListenableFuture> saveAsync(EventEntity entity, boolean ifNotExists) { if (entity.getId() == null) { entity.setId(UUIDs.timeBased()); } @@ -149,11 +185,13 @@ public class CassandraBaseEventDao extends CassandraAbstractSearchTimeDao { + if (rs.wasApplied()) { + return Optional.of(DaoUtil.getData(entity)); + } else { + return Optional.empty(); + } + }); } } 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 fb5c0fba8f..9469c61f63 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.event; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.TimePageLink; @@ -37,6 +38,14 @@ public interface EventDao extends Dao { */ Event save(Event event); + /** + * Save or update event object async + * + * @param event the event object + * @return saved event object future + */ + ListenableFuture saveAsync(Event event); + /** * Save event object if it is not yet saved * @@ -76,4 +85,16 @@ public interface EventDao extends Dao { * @return the event list */ List findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink); + + /** + * Find latest events by tenantId, entityId and eventType. + * + * @param tenantId the tenantId + * @param entityId the entityId + * @param eventType the eventType + * @param limit the limit + * @return the event list + */ + List findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/EventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/EventService.java index edee19033f..0698c6b0b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -15,18 +15,22 @@ */ package org.thingsboard.server.dao.event; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; +import java.util.List; import java.util.Optional; public interface EventService { Event save(Event event); + ListenableFuture saveAsync(Event event); + Optional saveIfNotExists(Event event); Optional findEvent(TenantId tenantId, EntityId entityId, String eventType, String eventUid); @@ -34,4 +38,7 @@ public interface EventService { TimePageData findEvents(TenantId tenantId, EntityId entityId, TimePageLink pageLink); TimePageData findEvents(TenantId tenantId, EntityId entityId, String eventType, TimePageLink pageLink); + + List findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java index 0aaf9c27dc..01ea41bf83 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseEntity.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.model; -import java.io.Serializable; import java.util.UUID; public interface BaseEntity extends ToData { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/EntitySubtypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/EntitySubtypeEntity.java index e4e4d61910..16622de604 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/EntitySubtypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/EntitySubtypeEntity.java @@ -25,7 +25,10 @@ import org.thingsboard.server.dao.model.type.EntityTypeCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TYPE_PROPERTY; @Table(name = ENTITY_SUBTYPE_COLUMN_FAMILY_NAME) public class EntitySubtypeEntity { 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 c2b55c956a..3a934eb5d2 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 @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.model; import com.datastax.driver.core.utils.UUIDs; import org.apache.commons.lang3.ArrayUtils; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.Aggregation; import java.util.UUID; @@ -29,6 +30,7 @@ public class ModelConstants { public static final UUID NULL_UUID = UUIDs.startOf(0); public static final String NULL_UUID_STR = UUIDConverter.fromTimeUUID(NULL_UUID); + public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); /** * Generic constants. @@ -272,21 +274,6 @@ public class ModelConstants { public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; - /** - * Cassandra plugin metadata constants. - */ - public static final String PLUGIN_COLUMN_FAMILY_NAME = "plugin"; - public static final String PLUGIN_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; - public static final String PLUGIN_NAME_PROPERTY = "name"; - public static final String PLUGIN_API_TOKEN_PROPERTY = "api_token"; - public static final String PLUGIN_CLASS_PROPERTY = "plugin_class"; - public static final String PLUGIN_ACCESS_PROPERTY = "public_access"; - public static final String PLUGIN_STATE_PROPERTY = STATE_PROPERTY; - public static final String PLUGIN_CONFIGURATION_PROPERTY = "configuration"; - - public static final String PLUGIN_BY_API_TOKEN_COLUMN_FAMILY_NAME = "plugin_by_api_token"; - public static final String PLUGIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "plugin_by_tenant_and_search_text"; - /** * Cassandra plugin component metadata constants. */ @@ -302,22 +289,6 @@ public class ModelConstants { public static final String COMPONENT_DESCRIPTOR_BY_SCOPE_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "component_desc_by_scope_type_search_text"; public static final String COMPONENT_DESCRIPTOR_BY_ID = "component_desc_by_id"; - /** - * Cassandra rule metadata constants. - */ - public static final String RULE_COLUMN_FAMILY_NAME = "rule"; - public static final String RULE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; - public static final String RULE_NAME_PROPERTY = "name"; - public static final String RULE_STATE_PROPERTY = STATE_PROPERTY; - public static final String RULE_WEIGHT_PROPERTY = "weight"; - public static final String RULE_PLUGIN_TOKEN_PROPERTY = "plugin_token"; - public static final String RULE_FILTERS = "filters"; - public static final String RULE_PROCESSOR = "processor"; - public static final String RULE_ACTION = "action"; - - public static final String RULE_BY_PLUGIN_TOKEN = "rule_by_plugin_token"; - public static final String RULE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_by_tenant_and_search_text"; - /** * Cassandra event constants. */ @@ -332,6 +303,29 @@ public class ModelConstants { public static final String EVENT_BY_TYPE_AND_ID_VIEW_NAME = "event_by_type_and_id"; public static final String EVENT_BY_ID_VIEW_NAME = "event_by_id"; + public static final String DEBUG_MODE = "debug_mode"; + + /** + * Cassandra rule chain constants. + */ + public static final String RULE_CHAIN_COLUMN_FAMILY_NAME = "rule_chain"; + public static final String RULE_CHAIN_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String RULE_CHAIN_NAME_PROPERTY = "name"; + public static final String RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY = "first_rule_node_id"; + public static final String RULE_CHAIN_ROOT_PROPERTY = "root"; + public static final String RULE_CHAIN_CONFIGURATION_PROPERTY = "configuration"; + + public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text"; + + /** + * Cassandra rule node constants. + */ + public static final String RULE_NODE_COLUMN_FAMILY_NAME = "rule_node"; + public static final String RULE_NODE_CHAIN_ID_PROPERTY = "rule_chain_id"; + public static final String RULE_NODE_TYPE_PROPERTY = "type"; + public static final String RULE_NODE_NAME_PROPERTY = "name"; + public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; + /** * Cassandra attributes and timeseries constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AdminSettingsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AdminSettingsEntity.java index 29c98594d3..7b8e8477eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AdminSettingsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AdminSettingsEntity.java @@ -29,7 +29,10 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_JSON_VALUE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Table(name = ADMIN_SETTINGS_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java index 72bf4df7e1..99df84fb67 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java @@ -16,7 +16,10 @@ package org.thingsboard.server.dao.model.nosql; import com.datastax.driver.core.utils.UUIDs; -import com.datastax.driver.mapping.annotations.*; +import com.datastax.driver.mapping.annotations.ClusteringColumn; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -35,7 +38,20 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_DETAILS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Table(name = ALARM_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java index 11eb90559c..da9bdb40eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java @@ -31,7 +31,14 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Table(name = ASSET_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AuditLogEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AuditLogEntity.java index ab2e3bce2d..aa145edb76 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AuditLogEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AuditLogEntity.java @@ -25,7 +25,11 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.id.AuditLogId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.type.ActionStatusCodec; import org.thingsboard.server.dao.model.type.ActionTypeCodec; @@ -34,7 +38,19 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_DATA_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_STATUS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_BY_ENTITY_ID_CF; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_USER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_USER_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Table(name = AUDIT_LOG_BY_ENTITY_ID_CF) @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/ComponentDescriptorEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/ComponentDescriptorEntity.java index 4998ae5efb..6593304df0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/ComponentDescriptorEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/ComponentDescriptorEntity.java @@ -28,7 +28,15 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_CLASS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_SCOPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COMPONENT_DESCRIPTOR_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; /** * @author Andrew Shvayka diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/CustomerEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/CustomerEntity.java index 09520371bb..d13a54e6f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/CustomerEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/CustomerEntity.java @@ -30,7 +30,20 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS2_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CITY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COUNTRY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_TITLE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EMAIL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.PHONE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.STATE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ZIP_PROPERTY; @Table(name = CUSTOMER_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java index 622d6df090..74963f8714 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java @@ -38,7 +38,13 @@ import java.io.IOException; import java.util.HashSet; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TITLE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java index ab0ec1c631..ce5f723d50 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java @@ -36,7 +36,12 @@ import java.io.IOException; import java.util.HashSet; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DASHBOARD_TITLE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceCredentialsEntity.java index 8483865311..5e4bb0f5e8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceCredentialsEntity.java @@ -30,7 +30,12 @@ import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CREDENTIALS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CREDENTIALS_CREDENTIALS_VALUE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CREDENTIALS_DEVICE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @Table(name = DEVICE_CREDENTIALS_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java index ef0c5fe159..3543840652 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DeviceEntity.java @@ -31,7 +31,14 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Table(name = DEVICE_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EventEntity.java index 3b55d384e7..7f123596aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EventEntity.java @@ -16,7 +16,10 @@ package org.thingsboard.server.dao.model.nosql; import com.datastax.driver.core.utils.UUIDs; -import com.datastax.driver.mapping.annotations.*; +import com.datastax.driver.mapping.annotations.ClusteringColumn; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.NoArgsConstructor; @@ -31,7 +34,14 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BODY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_UID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; /** * @author Andrew Shvayka diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/PluginMetaDataEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/PluginMetaDataEntity.java deleted file mode 100644 index eaf833fefa..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/PluginMetaDataEntity.java +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Copyright © 2016-2018 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.nosql; - -import com.datastax.driver.core.utils.UUIDs; -import com.datastax.driver.mapping.annotations.*; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; -import org.thingsboard.server.dao.model.type.JsonCodec; - -import java.util.Objects; -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.*; - -@Table(name = PLUGIN_COLUMN_FAMILY_NAME) -@EqualsAndHashCode -@ToString -public class PluginMetaDataEntity implements SearchTextEntity { - - @PartitionKey - @Column(name = ID_PROPERTY) - private UUID id; - - @Column(name = PLUGIN_API_TOKEN_PROPERTY) - private String apiToken; - - @ClusteringColumn - @Column(name = PLUGIN_TENANT_ID_PROPERTY) - private UUID tenantId; - - @Column(name = PLUGIN_NAME_PROPERTY) - private String name; - - @Column(name = PLUGIN_CLASS_PROPERTY) - private String clazz; - - @Column(name = PLUGIN_ACCESS_PROPERTY) - private boolean publicAccess; - - @Column(name = PLUGIN_STATE_PROPERTY, codec = ComponentLifecycleStateCodec.class) - private ComponentLifecycleState state; - - @Column(name = PLUGIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class) - private JsonNode configuration; - - @Column(name = SEARCH_TEXT_PROPERTY) - private String searchText; - - @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) - private JsonNode additionalInfo; - - public PluginMetaDataEntity() { - } - - public PluginMetaDataEntity(PluginMetaData pluginMetaData) { - if (pluginMetaData.getId() != null) { - this.id = pluginMetaData.getId().getId(); - } - this.tenantId = pluginMetaData.getTenantId().getId(); - this.apiToken = pluginMetaData.getApiToken(); - this.clazz = pluginMetaData.getClazz(); - this.name = pluginMetaData.getName(); - this.publicAccess = pluginMetaData.isPublicAccess(); - this.state = pluginMetaData.getState(); - this.configuration = pluginMetaData.getConfiguration(); - this.searchText = pluginMetaData.getName(); - this.additionalInfo = pluginMetaData.getAdditionalInfo(); - } - - @Override - public String getSearchTextSource() { - return getSearchText(); - } - - @Override - public void setSearchText(String searchText) { - this.searchText = searchText; - } - - @Override - public UUID getId() { - return id; - } - - @Override - public void setId(UUID id) { - this.id = id; - } - - public String getApiToken() { - return apiToken; - } - - public void setApiToken(String apiToken) { - this.apiToken = apiToken; - } - - public UUID getTenantId() { - return tenantId; - } - - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getClazz() { - return clazz; - } - - public void setClazz(String clazz) { - this.clazz = clazz; - } - - public JsonNode getConfiguration() { - return configuration; - } - - public void setConfiguration(JsonNode configuration) { - this.configuration = configuration; - } - - public boolean isPublicAccess() { - return publicAccess; - } - - public void setPublicAccess(boolean publicAccess) { - this.publicAccess = publicAccess; - } - - public ComponentLifecycleState getState() { - return state; - } - - public void setState(ComponentLifecycleState state) { - this.state = state; - } - - public String getSearchText() { - return searchText; - } - - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - - @Override - public PluginMetaData toData() { - PluginMetaData data = new PluginMetaData(new PluginId(id)); - data.setTenantId(new TenantId(tenantId)); - data.setCreatedTime(UUIDs.unixTimestamp(id)); - data.setName(name); - data.setConfiguration(configuration); - data.setClazz(clazz); - data.setPublicAccess(publicAccess); - data.setState(state); - data.setApiToken(apiToken); - data.setAdditionalInfo(additionalInfo); - return data; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java new file mode 100644 index 0000000000..f6299e8cd1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java @@ -0,0 +1,182 @@ +/** + * Copyright © 2016-2018 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.nosql; + +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.annotations.ClusteringColumn; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.model.type.JsonCodec; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEBUG_MODE; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_ROOT_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; + +@Table(name = RULE_CHAIN_COLUMN_FAMILY_NAME) +@EqualsAndHashCode +@ToString +public class RuleChainEntity implements SearchTextEntity { + + @PartitionKey + @Column(name = ID_PROPERTY) + private UUID id; + @ClusteringColumn + @Column(name = RULE_CHAIN_TENANT_ID_PROPERTY) + private UUID tenantId; + @Column(name = RULE_CHAIN_NAME_PROPERTY) + private String name; + @Column(name = SEARCH_TEXT_PROPERTY) + private String searchText; + @Column(name = RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY) + private UUID firstRuleNodeId; + @Column(name = RULE_CHAIN_ROOT_PROPERTY) + private boolean root; + @Getter + @Setter + @Column(name = DEBUG_MODE) + private boolean debugMode; + @Column(name = RULE_CHAIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class) + private JsonNode configuration; + @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) + private JsonNode additionalInfo; + + public RuleChainEntity() { + } + + public RuleChainEntity(RuleChain ruleChain) { + if (ruleChain.getId() != null) { + this.id = ruleChain.getUuidId(); + } + this.tenantId = DaoUtil.getId(ruleChain.getTenantId()); + this.name = ruleChain.getName(); + this.searchText = ruleChain.getName(); + this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId()); + this.root = ruleChain.isRoot(); + this.debugMode = ruleChain.isDebugMode(); + this.configuration = ruleChain.getConfiguration(); + this.additionalInfo = ruleChain.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return getSearchText(); + } + + @Override + public void setSearchText(String searchText) { + this.searchText = searchText; + } + + @Override + public UUID getId() { + return id; + } + + @Override + public void setId(UUID id) { + this.id = id; + } + + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public UUID getFirstRuleNodeId() { + return firstRuleNodeId; + } + + public void setFirstRuleNodeId(UUID firstRuleNodeId) { + this.firstRuleNodeId = firstRuleNodeId; + } + + public boolean isRoot() { + return root; + } + + public void setRoot(boolean root) { + this.root = root; + } + + public String getSearchText() { + return searchText; + } + + public JsonNode getConfiguration() { + return configuration; + } + + public void setConfiguration(JsonNode configuration) { + this.configuration = configuration; + } + + public JsonNode getAdditionalInfo() { + return additionalInfo; + } + + public void setAdditionalInfo(JsonNode additionalInfo) { + this.additionalInfo = additionalInfo; + } + + @Override + public RuleChain toData() { + RuleChain ruleChain = new RuleChain(new RuleChainId(id)); + ruleChain.setCreatedTime(UUIDs.unixTimestamp(id)); + ruleChain.setTenantId(new TenantId(tenantId)); + ruleChain.setName(name); + if (this.firstRuleNodeId != null) { + ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId)); + } + ruleChain.setRoot(this.root); + ruleChain.setDebugMode(this.debugMode); + ruleChain.setConfiguration(this.configuration); + ruleChain.setAdditionalInfo(this.additionalInfo); + return ruleChain; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleMetaDataEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleMetaDataEntity.java deleted file mode 100644 index 5e09581b55..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleMetaDataEntity.java +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright © 2016-2018 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.nosql; - -import com.datastax.driver.core.utils.UUIDs; -import com.datastax.driver.mapping.annotations.ClusteringColumn; -import com.datastax.driver.mapping.annotations.Column; -import com.datastax.driver.mapping.annotations.PartitionKey; -import com.datastax.driver.mapping.annotations.Table; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; -import org.thingsboard.server.dao.model.type.JsonCodec; - -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.*; - -@Table(name = RULE_COLUMN_FAMILY_NAME) -@EqualsAndHashCode -@ToString -public class RuleMetaDataEntity implements SearchTextEntity { - - @PartitionKey - @Column(name = ID_PROPERTY) - private UUID id; - @ClusteringColumn - @Column(name = RULE_TENANT_ID_PROPERTY) - private UUID tenantId; - @Column(name = RULE_NAME_PROPERTY) - private String name; - @Column(name = RULE_STATE_PROPERTY, codec = ComponentLifecycleStateCodec.class) - private ComponentLifecycleState state; - @Column(name = RULE_WEIGHT_PROPERTY) - private int weight; - @Column(name = SEARCH_TEXT_PROPERTY) - private String searchText; - @Column(name = RULE_PLUGIN_TOKEN_PROPERTY) - private String pluginToken; - @Column(name = RULE_FILTERS, codec = JsonCodec.class) - private JsonNode filters; - @Column(name = RULE_PROCESSOR, codec = JsonCodec.class) - private JsonNode processor; - @Column(name = RULE_ACTION, codec = JsonCodec.class) - private JsonNode action; - @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) - private JsonNode additionalInfo; - - public RuleMetaDataEntity() { - } - - public RuleMetaDataEntity(RuleMetaData rule) { - if (rule.getId() != null) { - this.id = rule.getUuidId(); - } - this.tenantId = DaoUtil.getId(rule.getTenantId()); - this.name = rule.getName(); - this.pluginToken = rule.getPluginToken(); - this.state = rule.getState(); - this.weight = rule.getWeight(); - this.searchText = rule.getName(); - this.filters = rule.getFilters(); - this.processor = rule.getProcessor(); - this.action = rule.getAction(); - this.additionalInfo = rule.getAdditionalInfo(); - } - - @Override - public String getSearchTextSource() { - return getSearchText(); - } - - @Override - public void setSearchText(String searchText) { - this.searchText = searchText; - } - - @Override - public UUID getId() { - return id; - } - - @Override - public void setId(UUID id) { - this.id = id; - } - - public UUID getTenantId() { - return tenantId; - } - - public void setTenantId(UUID tenantId) { - this.tenantId = tenantId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public ComponentLifecycleState getState() { - return state; - } - - public void setState(ComponentLifecycleState state) { - this.state = state; - } - - public int getWeight() { - return weight; - } - - public void setWeight(int weight) { - this.weight = weight; - } - - public String getPluginToken() { - return pluginToken; - } - - public void setPluginToken(String pluginToken) { - this.pluginToken = pluginToken; - } - - public String getSearchText() { - return searchText; - } - - public JsonNode getFilters() { - return filters; - } - - public void setFilters(JsonNode filters) { - this.filters = filters; - } - - public JsonNode getProcessor() { - return processor; - } - - public void setProcessor(JsonNode processor) { - this.processor = processor; - } - - public JsonNode getAction() { - return action; - } - - public void setAction(JsonNode action) { - this.action = action; - } - - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - - @Override - public RuleMetaData toData() { - RuleMetaData rule = new RuleMetaData(new RuleId(id)); - rule.setTenantId(new TenantId(tenantId)); - rule.setName(name); - rule.setState(state); - rule.setWeight(weight); - rule.setCreatedTime(UUIDs.unixTimestamp(id)); - rule.setPluginToken(pluginToken); - rule.setFilters(filters); - rule.setProcessor(processor); - rule.setAction(action); - rule.setAdditionalInfo(additionalInfo); - return rule; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java new file mode 100644 index 0000000000..39f721943c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java @@ -0,0 +1,167 @@ +/** + * Copyright © 2016-2018 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.nosql; + +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.model.type.JsonCodec; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEBUG_MODE; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_CHAIN_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_CONFIGURATION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; + +@Table(name = RULE_NODE_COLUMN_FAMILY_NAME) +@EqualsAndHashCode +@ToString +public class RuleNodeEntity implements SearchTextEntity { + + @PartitionKey + @Column(name = ID_PROPERTY) + private UUID id; + @Column(name = RULE_NODE_CHAIN_ID_PROPERTY) + private UUID ruleChainId; + @Column(name = RULE_NODE_TYPE_PROPERTY) + private String type; + @Column(name = RULE_NODE_NAME_PROPERTY) + private String name; + @Column(name = SEARCH_TEXT_PROPERTY) + private String searchText; + @Column(name = RULE_NODE_CONFIGURATION_PROPERTY, codec = JsonCodec.class) + private JsonNode configuration; + @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) + private JsonNode additionalInfo; + @Getter + @Setter + @Column(name = DEBUG_MODE) + private boolean debugMode; + + public RuleNodeEntity() { + } + + public RuleNodeEntity(RuleNode ruleNode) { + if (ruleNode.getId() != null) { + this.id = ruleNode.getUuidId(); + } + if (ruleNode.getRuleChainId() != null) { + this.ruleChainId = ruleNode.getRuleChainId().getId(); + } + this.type = ruleNode.getType(); + this.name = ruleNode.getName(); + this.debugMode = ruleNode.isDebugMode(); + this.searchText = ruleNode.getName(); + this.configuration = ruleNode.getConfiguration(); + this.additionalInfo = ruleNode.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return getSearchText(); + } + + @Override + public void setSearchText(String searchText) { + this.searchText = searchText; + } + + @Override + public UUID getId() { + return id; + } + + @Override + public void setId(UUID id) { + this.id = id; + } + + public UUID getRuleChainId() { + return ruleChainId; + } + + public void setRuleChainId(UUID ruleChainId) { + this.ruleChainId = ruleChainId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSearchText() { + return searchText; + } + + public JsonNode getConfiguration() { + return configuration; + } + + public void setConfiguration(JsonNode configuration) { + this.configuration = configuration; + } + + public JsonNode getAdditionalInfo() { + return additionalInfo; + } + + public void setAdditionalInfo(JsonNode additionalInfo) { + this.additionalInfo = additionalInfo; + } + + @Override + public RuleNode toData() { + RuleNode ruleNode = new RuleNode(new RuleNodeId(id)); + ruleNode.setCreatedTime(UUIDs.unixTimestamp(id)); + if (this.ruleChainId != null) { + ruleNode.setRuleChainId(new RuleChainId(this.ruleChainId)); + } + ruleNode.setType(this.type); + ruleNode.setName(this.name); + ruleNode.setDebugMode(this.debugMode); + ruleNode.setConfiguration(this.configuration); + ruleNode.setAdditionalInfo(this.additionalInfo); + return ruleNode; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/TenantEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/TenantEntity.java index 9140f9e22b..b16a73347b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/TenantEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/TenantEntity.java @@ -29,7 +29,20 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS2_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ADDRESS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.CITY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.COUNTRY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EMAIL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.PHONE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.STATE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_REGION_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_TITLE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ZIP_PROPERTY; @Table(name = TENANT_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserCredentialsEntity.java index ef441de282..1e7fbe2f89 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserCredentialsEntity.java @@ -27,7 +27,13 @@ import org.thingsboard.server.dao.model.BaseEntity; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_ACTIVATE_TOKEN_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_ENABLED_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_PASSWORD_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_RESET_TOKEN_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CREDENTIALS_USER_ID_PROPERTY; @Table(name = USER_CREDENTIALS_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserEntity.java index 69b5a630d6..f3f845dc53 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/UserEntity.java @@ -33,7 +33,16 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_AUTHORITY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.USER_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_EMAIL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_FIRST_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_LAST_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.USER_TENANT_ID_PROPERTY; @Table(name = USER_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetTypeEntity.java index 1d37aafa2b..fe2d76ebf5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetTypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetTypeEntity.java @@ -30,7 +30,13 @@ import org.thingsboard.server.dao.model.type.JsonCodec; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_DESCRIPTOR_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_TENANT_ID_PROPERTY; @Table(name = WIDGET_TYPE_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetsBundleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetsBundleEntity.java index 26cd88dc01..6704f1a0f0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetsBundleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/WidgetsBundleEntity.java @@ -30,7 +30,13 @@ import org.thingsboard.server.dao.model.SearchTextEntity; import java.nio.ByteBuffer; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_IMAGE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_TITLE_PROPERTY; @Table(name = WIDGETS_BUNDLE_COLUMN_FAMILY_NAME) @EqualsAndHashCode diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java index 7891bfc7e3..b5851f00bd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AdminSettingsEntity.java @@ -32,7 +32,9 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_JSON_VALUE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java index 65ca3869f4..32f95e15e9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java @@ -34,9 +34,24 @@ import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; -import javax.persistence.*; - -import static org.thingsboard.server.dao.model.ModelConstants.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; + +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java index a32f2d69f7..2da7396384 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java @@ -35,7 +35,12 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index 81722a1b2d..587a31485e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -17,13 +17,33 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; import java.io.Serializable; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ATTRIBUTE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.LAST_UPDATE_TS_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; @Data @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java index 6d135614d0..d4e8e278bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AuditLogEntity.java @@ -25,15 +25,33 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.id.AuditLogId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; -import javax.persistence.*; - -import static org.thingsboard.server.dao.model.ModelConstants.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; + +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_DATA_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_FAILURE_DETAILS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_STATUS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ACTION_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_USER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_USER_NAME_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java index 9739889171..66fbcc3382 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java @@ -29,7 +29,11 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.util.mapping.JsonStringType; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java index 676ae2d4bc..faa036eb63 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java @@ -26,7 +26,11 @@ import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java index 6f6b942bb5..aaf2c366f3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java @@ -31,9 +31,19 @@ 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.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_BODY_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EVENT_UID_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/PluginMetaDataEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/PluginMetaDataEntity.java deleted file mode 100644 index eec1dfc60a..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/PluginMetaDataEntity.java +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright © 2016-2018 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.datastax.driver.core.utils.UUIDs; -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.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.server.dao.util.mapping.JsonStringType; - -import javax.persistence.*; - -import static org.thingsboard.server.common.data.UUIDConverter.fromString; -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = ModelConstants.PLUGIN_COLUMN_FAMILY_NAME) -public class PluginMetaDataEntity extends BaseSqlEntity implements SearchTextEntity { - - @Column(name = ModelConstants.PLUGIN_API_TOKEN_PROPERTY) - private String apiToken; - - @Column(name = ModelConstants.PLUGIN_TENANT_ID_PROPERTY) - private String tenantId; - - @Column(name = ModelConstants.PLUGIN_NAME_PROPERTY) - private String name; - - @Column(name = ModelConstants.PLUGIN_CLASS_PROPERTY) - private String clazz; - - @Column(name = ModelConstants.PLUGIN_ACCESS_PROPERTY) - private boolean publicAccess; - - @Enumerated(EnumType.STRING) - @Column(name = ModelConstants.PLUGIN_STATE_PROPERTY) - private ComponentLifecycleState state; - - @Type(type = "json") - @Column(name = ModelConstants.PLUGIN_CONFIGURATION_PROPERTY) - private JsonNode configuration; - - @Type(type = "json") - @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) - private JsonNode additionalInfo; - - @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) - private String searchText; - - public PluginMetaDataEntity() { - } - - public PluginMetaDataEntity(PluginMetaData pluginMetaData) { - if (pluginMetaData.getId() != null) { - this.setId(pluginMetaData.getId().getId()); - } - this.tenantId = fromTimeUUID(pluginMetaData.getTenantId().getId()); - this.apiToken = pluginMetaData.getApiToken(); - this.clazz = pluginMetaData.getClazz(); - this.name = pluginMetaData.getName(); - this.publicAccess = pluginMetaData.isPublicAccess(); - this.state = pluginMetaData.getState(); - this.searchText = pluginMetaData.getName(); - this.configuration = pluginMetaData.getConfiguration(); - this.additionalInfo = pluginMetaData.getAdditionalInfo(); - } - - @Override - public String getSearchTextSource() { - return searchText; - } - - @Override - public void setSearchText(String searchText) { - this.searchText = searchText; - } - - @Override - public PluginMetaData toData() { - PluginMetaData data = new PluginMetaData(new PluginId(getId())); - data.setTenantId(new TenantId(fromString(tenantId))); - data.setCreatedTime(UUIDs.unixTimestamp(getId())); - data.setName(name); - data.setClazz(clazz); - data.setPublicAccess(publicAccess); - data.setState(state); - data.setApiToken(apiToken); - data.setConfiguration(configuration); - data.setAdditionalInfo(additionalInfo); - return data; - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationEntity.java index 2f94669506..6d0f6d0dfa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationEntity.java @@ -26,9 +26,20 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.model.ToData; import org.thingsboard.server.dao.util.mapping.JsonStringType; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADDITIONAL_INFO_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_GROUP_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_PROPERTY; @Data @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java new file mode 100644 index 0000000000..a48421a96a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java @@ -0,0 +1,116 @@ +/** + * Copyright © 2016-2018 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.datastax.driver.core.utils.UUIDs; +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.UUIDConverter; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME) +public class RuleChainEntity extends BaseSqlEntity implements SearchTextEntity { + + @Column(name = ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY) + private String tenantId; + + @Column(name = ModelConstants.RULE_CHAIN_NAME_PROPERTY) + private String name; + + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) + private String searchText; + + @Column(name = ModelConstants.RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY) + private String firstRuleNodeId; + + @Column(name = ModelConstants.RULE_CHAIN_ROOT_PROPERTY) + private boolean root; + + @Column(name = ModelConstants.DEBUG_MODE) + private boolean debugMode; + + @Type(type = "json") + @Column(name = ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY) + private JsonNode configuration; + + @Type(type = "json") + @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + public RuleChainEntity() { + } + + public RuleChainEntity(RuleChain ruleChain) { + if (ruleChain.getId() != null) { + this.setId(ruleChain.getUuidId()); + } + this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId())); + this.name = ruleChain.getName(); + this.searchText = ruleChain.getName(); + if (ruleChain.getFirstRuleNodeId() != null) { + this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId()); + } + this.root = ruleChain.isRoot(); + this.debugMode = ruleChain.isDebugMode(); + this.configuration = ruleChain.getConfiguration(); + this.additionalInfo = ruleChain.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return searchText; + } + + @Override + public void setSearchText(String searchText) { + this.searchText = searchText; + } + + @Override + public RuleChain toData() { + RuleChain ruleChain = new RuleChain(new RuleChainId(getId())); + ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId())); + ruleChain.setTenantId(new TenantId(toUUID(tenantId))); + ruleChain.setName(name); + if (firstRuleNodeId != null) { + ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId))); + } + ruleChain.setRoot(root); + ruleChain.setDebugMode(debugMode); + ruleChain.setConfiguration(configuration); + ruleChain.setAdditionalInfo(additionalInfo); + return ruleChain; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleMetaDataEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleMetaDataEntity.java deleted file mode 100644 index 26d790e656..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleMetaDataEntity.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright © 2016-2018 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.datastax.driver.core.utils.UUIDs; -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.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.server.dao.util.mapping.JsonStringType; - -import javax.persistence.*; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = ModelConstants.RULE_COLUMN_FAMILY_NAME) -public class RuleMetaDataEntity extends BaseSqlEntity implements SearchTextEntity { - - @Column(name = ModelConstants.RULE_TENANT_ID_PROPERTY) - private String tenantId; - - @Column(name = ModelConstants.RULE_NAME_PROPERTY) - private String name; - - @Enumerated(EnumType.STRING) - @Column(name = ModelConstants.RULE_STATE_PROPERTY) - private ComponentLifecycleState state; - - @Column(name = ModelConstants.RULE_WEIGHT_PROPERTY) - private int weight; - - @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) - private String searchText; - - @Column(name = ModelConstants.RULE_PLUGIN_TOKEN_PROPERTY) - private String pluginToken; - - @Type(type = "json") - @Column(name = ModelConstants.RULE_FILTERS) - private JsonNode filters; - - @Type(type = "json") - @Column(name = ModelConstants.RULE_PROCESSOR) - private JsonNode processor; - - @Type(type = "json") - @Column(name = ModelConstants.RULE_ACTION) - private JsonNode action; - - @Type(type = "json") - @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) - private JsonNode additionalInfo; - - public RuleMetaDataEntity() { - } - - public RuleMetaDataEntity(RuleMetaData rule) { - if (rule.getId() != null) { - this.setId(rule.getUuidId()); - } - this.tenantId = toString(DaoUtil.getId(rule.getTenantId())); - this.name = rule.getName(); - this.pluginToken = rule.getPluginToken(); - this.state = rule.getState(); - this.weight = rule.getWeight(); - this.searchText = rule.getName(); - this.filters = rule.getFilters(); - this.processor = rule.getProcessor(); - this.action = rule.getAction(); - this.additionalInfo = rule.getAdditionalInfo(); - } - - @Override - public String getSearchTextSource() { - return searchText; - } - - @Override - public void setSearchText(String searchText) { - this.searchText = searchText; - } - - @Override - public RuleMetaData toData() { - RuleMetaData rule = new RuleMetaData(new RuleId(getId())); - rule.setTenantId(new TenantId(toUUID(tenantId))); - rule.setName(name); - rule.setState(state); - rule.setWeight(weight); - rule.setCreatedTime(UUIDs.unixTimestamp(getId())); - rule.setPluginToken(pluginToken); - rule.setFilters(filters); - rule.setProcessor(processor); - rule.setAction(action); - rule.setAdditionalInfo(additionalInfo); - return rule; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java new file mode 100644 index 0000000000..1d0543341f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java @@ -0,0 +1,109 @@ +/** + * Copyright © 2016-2018 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.datastax.driver.core.utils.UUIDs; +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.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.RULE_NODE_COLUMN_FAMILY_NAME) +public class RuleNodeEntity extends BaseSqlEntity implements SearchTextEntity { + + @Column(name = ModelConstants.RULE_NODE_CHAIN_ID_PROPERTY) + private String ruleChainId; + + @Column(name = ModelConstants.RULE_NODE_TYPE_PROPERTY) + private String type; + + @Column(name = ModelConstants.RULE_NODE_NAME_PROPERTY) + private String name; + + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) + private String searchText; + + @Type(type = "json") + @Column(name = ModelConstants.RULE_NODE_CONFIGURATION_PROPERTY) + private JsonNode configuration; + + @Type(type = "json") + @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + @Column(name = ModelConstants.DEBUG_MODE) + private boolean debugMode; + + public RuleNodeEntity() { + } + + public RuleNodeEntity(RuleNode ruleNode) { + if (ruleNode.getId() != null) { + this.setId(ruleNode.getUuidId()); + } + if (ruleNode.getRuleChainId() != null) { + this.ruleChainId = toString(DaoUtil.getId(ruleNode.getRuleChainId())); + } + this.type = ruleNode.getType(); + this.name = ruleNode.getName(); + this.debugMode = ruleNode.isDebugMode(); + this.searchText = ruleNode.getName(); + this.configuration = ruleNode.getConfiguration(); + this.additionalInfo = ruleNode.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return searchText; + } + + @Override + public void setSearchText(String searchText) { + this.searchText = searchText; + } + + @Override + public RuleNode toData() { + RuleNode ruleNode = new RuleNode(new RuleNodeId(getId())); + ruleNode.setCreatedTime(UUIDs.unixTimestamp(getId())); + if (ruleChainId != null) { + ruleNode.setRuleChainId(new RuleChainId(toUUID(ruleChainId))); + } + ruleNode.setType(type); + ruleNode.setName(name); + ruleNode.setDebugMode(debugMode); + ruleNode.setConfiguration(configuration); + ruleNode.setAdditionalInfo(additionalInfo); + return ruleNode; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java index 3afe007879..a6d3ea6681 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java @@ -17,12 +17,31 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.*; - -import static org.thingsboard.server.dao.model.ModelConstants.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; + +import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; @Data @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvLatestEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvLatestEntity.java index 67267c94d4..cb7851c40d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvLatestEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvLatestEntity.java @@ -17,12 +17,31 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; @Data @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java index 3df2aa28da..7f18cbe746 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserEntity.java @@ -31,7 +31,11 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.util.mapping.JsonStringType; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; import static org.thingsboard.server.common.data.UUIDConverter.fromString; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java index 5c93066d90..d1af1677db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java @@ -15,20 +15,39 @@ */ package org.thingsboard.server.dao.nosql; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.CodecRegistry; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.exceptions.CodecNotFoundException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.dao.cassandra.CassandraCluster; -import org.thingsboard.server.dao.model.type.*; +import org.thingsboard.server.dao.model.type.AuthorityCodec; +import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; +import org.thingsboard.server.dao.model.type.ComponentScopeCodec; +import org.thingsboard.server.dao.model.type.ComponentTypeCodec; +import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; +import org.thingsboard.server.dao.model.type.EntityTypeCodec; +import org.thingsboard.server.dao.model.type.JsonCodec; import org.thingsboard.server.dao.util.BufferedRateLimiter; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + @Slf4j public abstract class CassandraAbstractDao { @Autowired protected CassandraCluster cluster; + private ConcurrentMap preparedStatementMap = new ConcurrentHashMap<>(); + @Autowired private BufferedRateLimiter rateLimiter; @@ -55,7 +74,7 @@ public abstract class CassandraAbstractDao { } protected PreparedStatement prepare(String query) { - return getSession().prepare(query); + return preparedStatementMap.computeIfAbsent(query, i -> getSession().prepare(i)); } private void registerCodecIfNotFound(CodecRegistry registry, TypeCodec codec) { @@ -83,15 +102,27 @@ public abstract class CassandraAbstractDao { } private ResultSet execute(Statement statement, ConsistencyLevel level) { - log.debug("Execute cassandra statement {}", statement); + if (log.isDebugEnabled()) { + log.debug("Execute cassandra statement {}", statementToString(statement)); + } return executeAsync(statement, level).getUninterruptibly(); } private ResultSetFuture executeAsync(Statement statement, ConsistencyLevel level) { - log.debug("Execute cassandra async statement {}", statement); + if (log.isDebugEnabled()) { + log.debug("Execute cassandra async statement {}", statementToString(statement)); + } if (statement.getConsistencyLevel() == null) { statement.setConsistencyLevel(level); } return new RateLimitedResultSetFuture(getSession(), rateLimiter, statement); } + + private static String statementToString(Statement statement) { + if (statement instanceof BoundStatement) { + return ((BoundStatement)statement).preparedStatement().getQueryString(); + } else { + return statement.toString(); + } + } } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java index d2505632d7..2b10bbcdbc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFuture.java @@ -19,7 +19,6 @@ import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Session; import com.datastax.driver.core.Statement; -import com.google.common.base.Function; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -28,7 +27,11 @@ import org.thingsboard.server.dao.exception.BufferLimitException; import org.thingsboard.server.dao.util.AsyncRateLimiter; import javax.annotation.Nullable; -import java.util.concurrent.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class RateLimitedResultSetFuture implements ResultSetFuture { @@ -36,14 +39,14 @@ public class RateLimitedResultSetFuture implements ResultSetFuture { private final ListenableFuture rateLimitFuture; public RateLimitedResultSetFuture(Session session, AsyncRateLimiter rateLimiter, Statement statement) { - this.rateLimitFuture = Futures.withFallback(rateLimiter.acquireAsync(), t -> { + this.rateLimitFuture = Futures.catchingAsync(rateLimiter.acquireAsync(), Throwable.class, t -> { if (!(t instanceof BufferLimitException)) { rateLimiter.release(); } return Futures.immediateFailedFuture(t); }); this.originalFuture = Futures.transform(rateLimitFuture, - (Function) i -> executeAsyncWithRelease(rateLimiter, session, statement)); + i -> executeAsyncWithRelease(rateLimiter, session, statement)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java deleted file mode 100644 index 67b8c603b2..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import com.google.common.util.concurrent.ListenableFuture; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.ComponentDescriptor; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.component.ComponentDescriptorService; -import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.DatabaseException; -import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.rule.RuleDao; -import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.service.Validator; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -import static org.thingsboard.server.dao.service.Validator.validateId; -@Service -@Slf4j -public class BasePluginService extends AbstractEntityService implements PluginService { - - //TODO: move to a better place. - public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); - - @Autowired - private PluginDao pluginDao; - - @Autowired - private RuleDao ruleDao; - - @Autowired - private ComponentDescriptorService componentDescriptorService; - - @Override - public PluginMetaData savePlugin(PluginMetaData plugin) { - pluginValidator.validate(plugin); - if (plugin.getTenantId() == null) { - log.trace("Save system plugin metadata with predefined id {}", SYSTEM_TENANT); - plugin.setTenantId(SYSTEM_TENANT); - } - if (plugin.getId() != null) { - PluginMetaData oldVersion = pluginDao.findById(plugin.getId()); - if (plugin.getState() == null) { - plugin.setState(oldVersion.getState()); - } else if (plugin.getState() != oldVersion.getState()) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the plugin!"); - } - } else { - if (plugin.getState() == null) { - plugin.setState(ComponentLifecycleState.SUSPENDED); - } else if (plugin.getState() != ComponentLifecycleState.SUSPENDED) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the plugin!"); - } - } - ComponentDescriptor descriptor = componentDescriptorService.findByClazz(plugin.getClazz()); - if (descriptor == null) { - throw new IncorrectParameterException("Plugin descriptor not found!"); - } else if (!ComponentType.PLUGIN.equals(descriptor.getType())) { - throw new IncorrectParameterException("Plugin class is actually " + descriptor.getType() + "!"); - } - PluginMetaData savedPlugin = pluginDao.findByApiToken(plugin.getApiToken()); - if (savedPlugin != null && (plugin.getId() == null || !savedPlugin.getId().getId().equals(plugin.getId().getId()))) { - throw new IncorrectParameterException("API token is already reserved!"); - } - if (!componentDescriptorService.validate(descriptor, plugin.getConfiguration())) { - throw new IncorrectParameterException("Filters configuration is not valid!"); - } - return pluginDao.save(plugin); - } - - @Override - public PluginMetaData findPluginById(PluginId pluginId) { - Validator.validateId(pluginId, "Incorrect plugin id for search request."); - return pluginDao.findById(pluginId); - } - - @Override - public ListenableFuture findPluginByIdAsync(PluginId pluginId) { - validateId(pluginId, "Incorrect plugin id for search plugin request."); - return pluginDao.findByIdAsync(pluginId.getId()); - } - - @Override - public PluginMetaData findPluginByApiToken(String apiToken) { - Validator.validateString(apiToken, "Incorrect plugin apiToken for search request."); - return pluginDao.findByApiToken(apiToken); - } - - @Override - public TextPageData findSystemPlugins(TextPageLink pageLink) { - Validator.validatePageLink(pageLink, "Incorrect PageLink object for search system plugin request."); - List plugins = pluginDao.findByTenantIdAndPageLink(SYSTEM_TENANT, pageLink); - return new TextPageData<>(plugins, pageLink); - } - - @Override - public TextPageData findTenantPlugins(TenantId tenantId, TextPageLink pageLink) { - Validator.validateId(tenantId, "Incorrect tenant id for search plugins request."); - Validator.validatePageLink(pageLink, "Incorrect PageLink object for search plugin request."); - List plugins = pluginDao.findByTenantIdAndPageLink(tenantId, pageLink); - return new TextPageData<>(plugins, pageLink); - } - - @Override - public List findSystemPlugins() { - log.trace("Executing findSystemPlugins"); - List plugins = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(300); - TextPageData pageData = null; - do { - pageData = findSystemPlugins(pageLink); - plugins.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - return plugins; - } - - @Override - public TextPageData findAllTenantPluginsByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.trace("Executing findAllTenantPluginsByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); - List plugins = pluginDao.findAllTenantPluginsByTenantId(tenantId.getId(), pageLink); - return new TextPageData<>(plugins, pageLink); - } - - @Override - public List findAllTenantPluginsByTenantId(TenantId tenantId) { - log.trace("Executing findAllTenantPluginsByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - List plugins = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(300); - TextPageData pageData = null; - do { - pageData = findAllTenantPluginsByTenantIdAndPageLink(tenantId, pageLink); - plugins.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - return plugins; - } - - @Override - public void activatePluginById(PluginId pluginId) { - updateLifeCycleState(pluginId, ComponentLifecycleState.ACTIVE); - } - - @Override - public void suspendPluginById(PluginId pluginId) { - PluginMetaData plugin = pluginDao.findById(pluginId); - List affectedRules = ruleDao.findRulesByPlugin(plugin.getApiToken()) - .stream().filter(rule -> rule.getState() == ComponentLifecycleState.ACTIVE).collect(Collectors.toList()); - if (affectedRules.isEmpty()) { - updateLifeCycleState(pluginId, ComponentLifecycleState.SUSPENDED); - } else { - throw new DataValidationException("Can't suspend plugin that has active rules!"); - } - } - - private void updateLifeCycleState(PluginId pluginId, ComponentLifecycleState state) { - Validator.validateId(pluginId, "Incorrect plugin id for state change request."); - PluginMetaData plugin = pluginDao.findById(pluginId); - if (plugin != null) { - plugin.setState(state); - pluginDao.save(plugin); - } else { - throw new DatabaseException("Plugin not found!"); - } - } - - @Override - public void deletePluginById(PluginId pluginId) { - Validator.validateId(pluginId, "Incorrect plugin id for delete request."); - deleteEntityRelations(pluginId); - checkRulesAndDelete(pluginId.getId()); - } - - private void checkRulesAndDelete(UUID pluginId) { - PluginMetaData plugin = pluginDao.findById(pluginId); - List affectedRules = ruleDao.findRulesByPlugin(plugin.getApiToken()); - if (affectedRules.isEmpty()) { - pluginDao.deleteById(pluginId); - } else { - throw new DataValidationException("Plugin deletion will affect existing rules!"); - } - } - - @Override - public void deletePluginsByTenantId(TenantId tenantId) { - Validator.validateId(tenantId, "Incorrect tenant id for delete plugins request."); - tenantPluginRemover.removeEntities(tenantId); - } - - - private DataValidator pluginValidator = - new DataValidator() { - @Override - protected void validateDataImpl(PluginMetaData plugin) { - if (StringUtils.isEmpty(plugin.getName())) { - throw new DataValidationException("Plugin name should be specified!."); - } - if (StringUtils.isEmpty(plugin.getClazz())) { - throw new DataValidationException("Plugin clazz should be specified!."); - } - if (StringUtils.isEmpty(plugin.getApiToken())) { - throw new DataValidationException("Plugin api token is not set!"); - } - if (plugin.getConfiguration() == null) { - throw new DataValidationException("Plugin configuration is not set!"); - } - } - }; - - private PaginatedRemover tenantPluginRemover = - new PaginatedRemover() { - - @Override - protected List findEntities(TenantId id, TextPageLink pageLink) { - return pluginDao.findByTenantIdAndPageLink(id, pageLink); - } - - @Override - protected void removeEntity(PluginMetaData entity) { - checkRulesAndDelete(entity.getUuidId()); - } - }; -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/CassandraBasePluginDao.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/CassandraBasePluginDao.java deleted file mode 100644 index 7b948a2d54..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/plugin/CassandraBasePluginDao.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import com.datastax.driver.core.querybuilder.Select; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.model.nosql.PluginMetaDataEntity; -import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; -import org.thingsboard.server.dao.util.NoSqlDao; - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; - -@Component -@Slf4j -@NoSqlDao -public class CassandraBasePluginDao extends CassandraAbstractSearchTextDao implements PluginDao { - - public static final String SEARCH_RESULT = "Search result: [{}]"; - - @Override - protected Class getColumnFamilyClass() { - return PluginMetaDataEntity.class; - } - - @Override - protected String getColumnFamilyName() { - return ModelConstants.PLUGIN_COLUMN_FAMILY_NAME; - } - - @Override - public PluginMetaData findById(PluginId pluginId) { - log.debug("Search plugin meta-data entity by id [{}]", pluginId); - PluginMetaData pluginMetaData = super.findById(pluginId.getId()); - if (log.isTraceEnabled()) { - log.trace("Search result: [{}] for plugin entity [{}]", pluginMetaData != null, pluginMetaData); - } else { - log.debug(SEARCH_RESULT, pluginMetaData != null); - } - return pluginMetaData; - } - - @Override - public PluginMetaData findByApiToken(String apiToken) { - log.debug("Search plugin meta-data entity by api token [{}]", apiToken); - Select.Where query = select().from(ModelConstants.PLUGIN_BY_API_TOKEN_COLUMN_FAMILY_NAME).where(eq(ModelConstants.PLUGIN_API_TOKEN_PROPERTY, apiToken)); - log.trace("Execute query [{}]", query); - PluginMetaDataEntity entity = findOneByStatement(query); - if (log.isTraceEnabled()) { - log.trace("Search result: [{}] for plugin entity [{}]", entity != null, entity); - } else { - log.debug(SEARCH_RESULT, entity != null); - } - return DaoUtil.getData(entity); - } - - @Override - public void deleteById(UUID id) { - log.debug("Delete plugin meta-data entity by id [{}]", id); - boolean result = removeById(id); - log.debug("Delete result: [{}]", result); - } - - @Override - public void deleteById(PluginId pluginId) { - deleteById(pluginId.getId()); - } - - @Override - public List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.debug("Try to find plugins by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = findPageWithTextSearch(ModelConstants.PLUGIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, - Arrays.asList(eq(ModelConstants.PLUGIN_TENANT_ID_PROPERTY, tenantId.getId())), pageLink); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public List findAllTenantPluginsByTenantId(UUID tenantId, TextPageLink pageLink) { - log.debug("Try to find all tenant plugins by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List pluginEntities = findPageWithTextSearch(ModelConstants.PLUGIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, - Arrays.asList(in(ModelConstants.PLUGIN_TENANT_ID_PROPERTY, Arrays.asList(NULL_UUID, tenantId))), - pageLink); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(pluginEntities.toArray())); - } else { - log.debug(SEARCH_RESULT, pluginEntities.size()); - } - return DaoUtil.convertDataList(pluginEntities); - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginDao.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginDao.java deleted file mode 100644 index b86b983368..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginDao.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.Dao; - -import java.util.List; -import java.util.UUID; - -public interface PluginDao extends Dao { - - PluginMetaData save(PluginMetaData plugin); - - PluginMetaData findById(PluginId pluginId); - - PluginMetaData findByApiToken(String apiToken); - - void deleteById(UUID id); - - void deleteById(PluginId pluginId); - - List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink); - - /** - * Find all tenant plugins (including system) by tenantId and page link. - * - * @param tenantId the tenantId - * @param pageLink the page link - * @return the list of plugins objects - */ - List findAllTenantPluginsByTenantId(UUID tenantId, TextPageLink pageLink); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java b/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java deleted file mode 100644 index 106ad8e2a0..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/plugin/PluginService.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; - -import java.util.List; - -public interface PluginService { - - PluginMetaData savePlugin(PluginMetaData plugin); - - PluginMetaData findPluginById(PluginId pluginId); - - ListenableFuture findPluginByIdAsync(PluginId pluginId); - - PluginMetaData findPluginByApiToken(String apiToken); - - TextPageData findSystemPlugins(TextPageLink pageLink); - - TextPageData findTenantPlugins(TenantId tenantId, TextPageLink pageLink); - - List findSystemPlugins(); - - TextPageData findAllTenantPluginsByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink); - - List findAllTenantPluginsByTenantId(TenantId tenantId); - - void activatePluginById(PluginId pluginId); - - void suspendPluginById(PluginId pluginId); - - void deletePluginById(PluginId pluginId); - - void deletePluginsByTenantId(TenantId tenantId); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcResponsePluginToRuleMsg.java b/dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java similarity index 57% rename from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcResponsePluginToRuleMsg.java rename to dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java index 0d6526e857..19021eb95c 100644 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcResponsePluginToRuleMsg.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/MsgQueue.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.api.plugins.msg; +package org.thingsboard.server.dao.queue; -import org.thingsboard.server.common.data.id.RuleId; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; +import org.thingsboard.server.common.msg.TbMsg; import java.util.UUID; -public class RpcResponsePluginToRuleMsg extends AbstractPluginToRuleMsg { +public interface MsgQueue { - private static final long serialVersionUID = 1L; + ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); - public RpcResponsePluginToRuleMsg(UUID uid, TenantId tenantId, RuleId ruleId, ToServerRpcResponseMsg payload) { - super(uid, tenantId, ruleId, payload); - } + ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition); + + Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition); + + ListenableFuture cleanUp(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java new file mode 100644 index 0000000000..ca61a63a4c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueBenchmark.java @@ -0,0 +1,152 @@ +/** + * Copyright © 2016-2018 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.queue; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.HostDistance; +import com.datastax.driver.core.PoolingOptions; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.utils.UUIDs; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.context.annotation.Bean; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import javax.annotation.Nullable; +import java.net.InetSocketAddress; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +//@SpringBootApplication +//@EnableAutoConfiguration +//@ComponentScan({"org.thingsboard.rule.engine"}) +//@PropertySource("classpath:processing-pipeline.properties") +@Slf4j +public class QueueBenchmark implements CommandLineRunner { + + public static void main(String[] args) { + try { + SpringApplication.run(QueueBenchmark.class, args); + } catch (Throwable th) { + th.printStackTrace(); + System.exit(0); + } + } + + @Autowired + private MsgQueue msgQueue; + + @Override + public void run(String... strings) throws Exception { + System.out.println("It works + " + msgQueue); + + + long start = System.currentTimeMillis(); + int msgCount = 10000000; + AtomicLong count = new AtomicLong(0); + ExecutorService service = Executors.newFixedThreadPool(100); + + CountDownLatch latch = new CountDownLatch(msgCount); + for (int i = 0; i < msgCount; i++) { + service.submit(() -> { + boolean isFinished = false; + while (!isFinished) { + try { + TbMsg msg = randomMsg(); + UUID nodeId = UUIDs.timeBased(); + ListenableFuture put = msgQueue.put(new TenantId(EntityId.NULL_UUID), msg, nodeId, 100L); +// ListenableFuture put = msgQueue.ack(msg, nodeId, 100L); + Futures.addCallback(put, new FutureCallback() { + @Override + public void onSuccess(@Nullable Void result) { + latch.countDown(); + } + + @Override + public void onFailure(Throwable t) { +// t.printStackTrace(); + System.out.println("onFailure, because:" + t.getMessage()); + latch.countDown(); + } + }); + isFinished = true; + } catch (Throwable th) { +// th.printStackTrace(); + System.out.println("Repeat query, because:" + th.getMessage()); +// latch.countDown(); + } + } + }); + } + + long prev = 0L; + while (latch.getCount() != 0) { + TimeUnit.SECONDS.sleep(1); + long curr = latch.getCount(); + long rps = prev - curr; + prev = curr; + System.out.println("rps = " + rps); + } + + long end = System.currentTimeMillis(); + System.out.println("final rps = " + (msgCount / (end - start) * 1000)); + + System.out.println("Finished"); + + } + + private TbMsg randomMsg() { + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("key", "value"); + String dataStr = "someContent"; + return new TbMsg(UUIDs.timeBased(), "type", null, metaData, TbMsgDataType.JSON, dataStr, new RuleChainId(UUIDs.timeBased()), new RuleNodeId(UUIDs.timeBased()), 0L); + } + + @Bean + public Session session() { + Cluster thingsboard = Cluster.builder() + .addContactPointsWithPorts(new InetSocketAddress("127.0.0.1", 9042)) + .withClusterName("thingsboard") +// .withSocketOptions(socketOpts.getOpts()) + .withPoolingOptions(new PoolingOptions() + .setMaxRequestsPerConnection(HostDistance.LOCAL, 32768) + .setMaxRequestsPerConnection(HostDistance.REMOTE, 32768)).build(); + + Session session = thingsboard.connect("thingsboard"); + return session; + } + + @Bean + public int defaultTtl() { + return 6000; + } + +} diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/DeviceAttributesFilterConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java similarity index 76% rename from extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/DeviceAttributesFilterConfiguration.java rename to dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java index 76fcef1dbc..bb44a2b192 100644 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/filter/DeviceAttributesFilterConfiguration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgKey.java @@ -13,15 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.extensions.core.filter; +package org.thingsboard.server.dao.queue.memory; import lombok.Data; +import java.util.UUID; + /** - * @author Andrew Shvayka + * Created by ashvayka on 30.04.18. */ @Data -public class DeviceAttributesFilterConfiguration { - - private String filterBody; +public final class InMemoryMsgKey { + final UUID nodeId; + final long clusterPartition; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java new file mode 100644 index 0000000000..93057784f6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/memory/InMemoryMsgQueue.java @@ -0,0 +1,123 @@ +/** + * Copyright © 2016-2018 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.queue.memory; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.dao.queue.MsgQueue; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; + +/** + * Created by ashvayka on 27.04.18. + */ +@Component +@ConditionalOnProperty(prefix = "actors.rule.queue", value = "type", havingValue = "memory", matchIfMissing = true) +@Slf4j +public class InMemoryMsgQueue implements MsgQueue { + + private ListeningExecutorService queueExecutor; + private Map>> data = new HashMap<>(); + + @PostConstruct + public void init() { + // Should be always single threaded due to absence of locks. + queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + } + + @PreDestroy + public void stop() { + if (queueExecutor != null) { + queueExecutor.shutdownNow(); + } + } + + @Override + public ListenableFuture put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { + return queueExecutor.submit(() -> { + data.computeIfAbsent(tenantId, key -> new HashMap<>()). + computeIfAbsent(new InMemoryMsgKey(nodeId, clusterPartition), key -> new HashMap<>()).put(msg.getId(), msg); + return null; + }); + } + + @Override + public ListenableFuture ack(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { + return queueExecutor.submit(() -> { + Map> tenantMap = data.get(tenantId); + if (tenantMap != null) { + InMemoryMsgKey key = new InMemoryMsgKey(nodeId, clusterPartition); + Map map = tenantMap.get(key); + if (map != null) { + map.remove(msg.getId()); + if (map.isEmpty()) { + tenantMap.remove(key); + } + } + if (tenantMap.isEmpty()) { + data.remove(tenantId); + } + } + return null; + }); + } + + @Override + public Iterable findUnprocessed(TenantId tenantId, UUID nodeId, long clusterPartition) { + ListenableFuture> list = queueExecutor.submit(() -> { + Map> tenantMap = data.get(tenantId); + if (tenantMap != null) { + InMemoryMsgKey key = new InMemoryMsgKey(nodeId, clusterPartition); + Map map = tenantMap.get(key); + if (map != null) { + return new ArrayList<>(map.values()); + } else { + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } + }); + try { + return list.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public ListenableFuture cleanUp(TenantId tenantId) { + return queueExecutor.submit(() -> { + data.remove(tenantId); + return null; + }); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java index 55838d6462..ba6d09d754 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.relation; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Row; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.fasterxml.jackson.databind.JsonNode; diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index e9f808aee1..ea29f6fa77 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -29,12 +29,23 @@ import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.relation.*; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationInfo; +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.EntityTypeFilter; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.function.BiConsumer; @@ -166,129 +177,96 @@ public class BaseRelationService implements RelationService { } @Override - public boolean deleteEntityRelations(EntityId entity) { - Cache cache = cacheManager.getCache(RELATIONS_CACHE); - log.trace("Executing deleteEntityRelations [{}]", entity); - validate(entity); - List>> inboundRelationsList = new ArrayList<>(); - for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { - inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); - } - ListenableFuture>> inboundRelations = Futures.allAsList(inboundRelationsList); - ListenableFuture> inboundDeletions = Futures.transform(inboundRelations, (List> relations) -> - getBooleans(relations, cache, true)); - - ListenableFuture inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); - boolean inboundDeleteResult = false; + public void deleteEntityRelations(EntityId entityId) { try { - inboundDeleteResult = inboundFuture.get(); + deleteEntityRelationsAsync(entityId).get(); } catch (InterruptedException | ExecutionException e) { - log.error("Error deleting entity inbound relations", e); - } - - List>> outboundRelationsList = new ArrayList<>(); - for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { - outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); - } - ListenableFuture>> outboundRelations = Futures.allAsList(outboundRelationsList); - Futures.transform(outboundRelations, (Function>, List>) relations -> - getBooleans(relations, cache, false)); - - boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity); - return inboundDeleteResult && outboundDeleteResult; - } - - private List getBooleans(List> relations, Cache cache, boolean isRemove) { - List results = new ArrayList<>(); - for (List relationList : relations) { - relationList.stream().forEach(relation -> checkFromDeleteSync(cache, results, relation, isRemove)); - } - return results; - } - - private void checkFromDeleteSync(Cache cache, List results, EntityRelation relation, boolean isRemove) { - if (isRemove) { - results.add(relationDao.deleteRelation(relation)); + throw new RuntimeException(e); } - cacheEviction(relation, cache); } @Override - public ListenableFuture deleteEntityRelationsAsync(EntityId entity) { + public ListenableFuture deleteEntityRelationsAsync(EntityId entityId) { Cache cache = cacheManager.getCache(RELATIONS_CACHE); - log.trace("Executing deleteEntityRelationsAsync [{}]", entity); - validate(entity); + log.trace("Executing deleteEntityRelationsAsync [{}]", entityId); + validate(entityId); List>> inboundRelationsList = new ArrayList<>(); for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { - inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); + inboundRelationsList.add(relationDao.findAllByTo(entityId, typeGroup)); } - ListenableFuture>> inboundRelations = Futures.allAsList(inboundRelationsList); - ListenableFuture> inboundDeletions = Futures.transform(inboundRelations, - (AsyncFunction>, List>) relations -> { - List> results = getListenableFutures(relations, cache, true); - return Futures.allAsList(results); - }); - ListenableFuture inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); + ListenableFuture>> inboundRelations = Futures.allAsList(inboundRelationsList); List>> outboundRelationsList = new ArrayList<>(); for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { - outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); + outboundRelationsList.add(relationDao.findAllByFrom(entityId, typeGroup)); } + ListenableFuture>> outboundRelations = Futures.allAsList(outboundRelationsList); - Futures.transform(outboundRelations, (AsyncFunction>, List>) relations -> { - List> results = getListenableFutures(relations, cache, false); - return Futures.allAsList(results); - }); - ListenableFuture outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); - return Futures.transform(Futures.allAsList(Arrays.asList(inboundFuture, outboundFuture)), getListToBooleanFunction()); + ListenableFuture> inboundDeletions = Futures.transformAsync(inboundRelations, + relations -> { + List> results = deleteRelationGroupsAsync(relations, cache, true); + return Futures.allAsList(results); + }); + + ListenableFuture> outboundDeletions = Futures.transformAsync(outboundRelations, + relations -> { + List> results = deleteRelationGroupsAsync(relations, cache, false); + return Futures.allAsList(results); + }); + + ListenableFuture>> deletionsFuture = Futures.allAsList(inboundDeletions, outboundDeletions); + + return Futures.transform(Futures.transformAsync(deletionsFuture, (deletions) -> relationDao.deleteOutboundRelationsAsync(entityId)), result -> null); } - private List> getListenableFutures(List> relations, Cache cache, boolean isRemove) { + private List> deleteRelationGroupsAsync(List> relations, Cache cache, boolean deleteFromDb) { List> results = new ArrayList<>(); for (List relationList : relations) { - relationList.stream().forEach(relation -> checkFromDeleteAsync(cache, results, relation, isRemove)); + relationList.forEach(relation -> results.add(deleteAsync(cache, relation, deleteFromDb))); } return results; } - private void checkFromDeleteAsync(Cache cache, List> results, EntityRelation relation, boolean isRemove) { - if (isRemove) { - results.add(relationDao.deleteRelationAsync(relation)); - } + private ListenableFuture deleteAsync(Cache cache, EntityRelation relation, boolean deleteFromDb) { cacheEviction(relation, cache); + if (deleteFromDb) { + return relationDao.deleteRelationAsync(relation); + } else { + return Futures.immediateFuture(false); + } } private void cacheEviction(EntityRelation relation, Cache cache) { - List toAndGroup = new ArrayList<>(); - toAndGroup.add(relation.getTo()); - toAndGroup.add(relation.getTypeGroup()); - cache.evict(toAndGroup); - - List toTypeAndGroup = new ArrayList<>(); - toTypeAndGroup.add(relation.getTo()); - toTypeAndGroup.add(relation.getType()); - toTypeAndGroup.add(relation.getTypeGroup()); - cache.evict(toTypeAndGroup); - - List fromAndGroup = new ArrayList<>(); - fromAndGroup.add(relation.getFrom()); - fromAndGroup.add(relation.getTypeGroup()); - cache.evict(fromAndGroup); - - List fromTypeAndGroup = new ArrayList<>(); - fromTypeAndGroup.add(relation.getFrom()); - fromTypeAndGroup.add(relation.getType()); - fromTypeAndGroup.add(relation.getTypeGroup()); - cache.evict(fromTypeAndGroup); - - List fromToTypeAndGroup = new ArrayList<>(); - fromToTypeAndGroup.add(relation.getFrom()); - fromToTypeAndGroup.add(relation.getTo()); - fromToTypeAndGroup.add(relation.getType()); - fromToTypeAndGroup.add(relation.getTypeGroup()); - cache.evict(fromToTypeAndGroup); + List fromToTypeAndTypeGroup = new ArrayList<>(); + fromToTypeAndTypeGroup.add(relation.getFrom()); + fromToTypeAndTypeGroup.add(relation.getTo()); + fromToTypeAndTypeGroup.add(relation.getType()); + fromToTypeAndTypeGroup.add(relation.getTypeGroup()); + cache.evict(fromToTypeAndTypeGroup); + + List fromTypeAndTypeGroup = new ArrayList<>(); + fromTypeAndTypeGroup.add(relation.getFrom()); + fromTypeAndTypeGroup.add(relation.getType()); + fromTypeAndTypeGroup.add(relation.getTypeGroup()); + cache.evict(fromTypeAndTypeGroup); + + List fromAndTypeGroup = new ArrayList<>(); + fromAndTypeGroup.add(relation.getFrom()); + fromAndTypeGroup.add(relation.getTypeGroup()); + cache.evict(fromAndTypeGroup); + + List toAndTypeGroup = new ArrayList<>(); + toAndTypeGroup.add(relation.getTo()); + toAndTypeGroup.add(relation.getTypeGroup()); + cache.evict(toAndTypeGroup); + + List toTypeAndTypeGroup = new ArrayList<>(); + fromTypeAndTypeGroup.add(relation.getTo()); + fromTypeAndTypeGroup.add(relation.getType()); + fromTypeAndTypeGroup.add(relation.getTypeGroup()); + cache.evict(toTypeAndTypeGroup); } @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}") @@ -315,17 +293,16 @@ public class BaseRelationService implements RelationService { validate(from); validateTypeGroup(typeGroup); ListenableFuture> relations = relationDao.findAllByFrom(from, typeGroup); - ListenableFuture> relationsInfo = Futures.transform(relations, - (AsyncFunction, List>) relations1 -> { + return Futures.transformAsync(relations, + relations1 -> { List> futures = new ArrayList<>(); - relations1.stream().forEach(relation -> + relations1.forEach(relation -> futures.add(fetchRelationInfoAsync(relation, - relation2 -> relation2.getTo(), - (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setToName(entityName))) + EntityRelation::getTo, + EntityRelationInfo::setToName)) ); return Futures.successfulAsList(futures); }); - return relationsInfo; } @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}") @@ -371,30 +348,27 @@ public class BaseRelationService implements RelationService { validate(to); validateTypeGroup(typeGroup); ListenableFuture> relations = relationDao.findAllByTo(to, typeGroup); - ListenableFuture> relationsInfo = Futures.transform(relations, - (AsyncFunction, List>) relations1 -> { + return Futures.transformAsync(relations, + relations1 -> { List> futures = new ArrayList<>(); - relations1.stream().forEach(relation -> + relations1.forEach(relation -> futures.add(fetchRelationInfoAsync(relation, - relation2 -> relation2.getFrom(), - (EntityRelationInfo relationInfo, String entityName) -> relationInfo.setFromName(entityName))) + EntityRelation::getFrom, + EntityRelationInfo::setFromName)) ); return Futures.successfulAsList(futures); }); - return relationsInfo; } private ListenableFuture fetchRelationInfoAsync(EntityRelation relation, Function entityIdGetter, BiConsumer entityNameSetter) { ListenableFuture entityName = entityService.fetchEntityNameAsync(entityIdGetter.apply(relation)); - ListenableFuture entityRelationInfo = - Futures.transform(entityName, (Function) entityName1 -> { - EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); - entityNameSetter.accept(entityRelationInfo1, entityName1); - return entityRelationInfo1; - }); - return entityRelationInfo; + return Futures.transform(entityName, entityName1 -> { + EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation); + entityNameSetter.accept(entityRelationInfo1, entityName1); + return entityRelationInfo1; + }); } @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") @@ -429,7 +403,7 @@ public class BaseRelationService implements RelationService { try { ListenableFuture> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), maxLvl, new ConcurrentHashMap<>()); - return Futures.transform(relationSet, (Function, List>) input -> { + return Futures.transform(relationSet, input -> { List relations = new ArrayList<>(); if (filters == null || filters.isEmpty()) { relations.addAll(input); @@ -453,10 +427,10 @@ public class BaseRelationService implements RelationService { log.trace("Executing findInfoByQuery [{}]", query); ListenableFuture> relations = findByQuery(query); EntitySearchDirection direction = query.getParameters().getDirection(); - ListenableFuture> relationsInfo = Futures.transform(relations, - (AsyncFunction, List>) relations1 -> { + return Futures.transformAsync(relations, + relations1 -> { List> futures = new ArrayList<>(); - relations1.stream().forEach(relation -> + relations1.forEach(relation -> futures.add(fetchRelationInfoAsync(relation, relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(), (EntityRelationInfo relationInfo, String entityName) -> { @@ -469,7 +443,6 @@ public class BaseRelationService implements RelationService { ); return Futures.successfulAsList(futures); }); - return relationsInfo; } protected void validate(EntityRelation relation) { @@ -575,7 +548,7 @@ public class BaseRelationService implements RelationService { } //TODO: try to remove this blocking operation List> relations = Futures.successfulAsList(futures).get(); - relations.forEach(r -> r.forEach(d -> children.add(d))); + relations.forEach(r -> r.forEach(children::add)); return Futures.immediateFuture(children); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index ca1b959c95..da58b11a25 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import java.util.List; +import java.util.concurrent.ExecutionException; /** * Created by ashvayka on 27.04.17. @@ -47,9 +48,9 @@ public interface RelationService { ListenableFuture deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); - boolean deleteEntityRelations(EntityId entity); + void deleteEntityRelations(EntityId entity); - ListenableFuture deleteEntityRelationsAsync(EntityId entity); + ListenableFuture deleteEntityRelationsAsync(EntityId entity); List findByFrom(EntityId from, RelationTypeGroup typeGroup); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java new file mode 100644 index 0000000000..14de1a45e3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -0,0 +1,375 @@ +/** + * Copyright © 2016-2018 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.rule; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.NodeConnectionInfo; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +/** + * Created by igor on 3/12/18. + */ +@Service +@Slf4j +public class BaseRuleChainService extends AbstractEntityService implements RuleChainService { + + @Autowired + private RuleChainDao ruleChainDao; + + @Autowired + private RuleNodeDao ruleNodeDao; + + @Autowired + private TenantDao tenantDao; + + @Override + public RuleChain saveRuleChain(RuleChain ruleChain) { + ruleChainValidator.validate(ruleChain); + RuleChain savedRuleChain = ruleChainDao.save(ruleChain); + if (ruleChain.isRoot() && ruleChain.getId() == null) { + try { + createRelation(new EntityRelation(savedRuleChain.getTenantId(), savedRuleChain.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create tenant to root rule chain relation. from: [{}], to: [{}]", + savedRuleChain.getTenantId(), savedRuleChain.getId()); + throw new RuntimeException(e); + } + } + return savedRuleChain; + } + + @Override + public boolean setRootRuleChain(RuleChainId ruleChainId) { + RuleChain ruleChain = ruleChainDao.findById(ruleChainId.getId()); + if (!ruleChain.isRoot()) { + RuleChain previousRootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); + if (!previousRootRuleChain.getId().equals(ruleChain.getId())) { + try { + deleteRelation(new EntityRelation(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + previousRootRuleChain.setRoot(false); + ruleChainDao.save(previousRootRuleChain); + createRelation(new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + ruleChain.setRoot(true); + ruleChainDao.save(ruleChain); + return true; + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to set root rule chain, ruleChainId: [{}]", ruleChainId); + throw new RuntimeException(e); + } + } + } + return false; + } + + @Override + public RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMetaData) { + Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id."); + RuleChain ruleChain = findRuleChainById(ruleChainMetaData.getRuleChainId()); + if (ruleChain == null) { + return null; + } + + List nodes = ruleChainMetaData.getNodes(); + List toAddOrUpdate = new ArrayList<>(); + List toDelete = new ArrayList<>(); + + Map ruleNodeIndexMap = new HashMap<>(); + if (nodes != null) { + for (RuleNode node : nodes) { + if (node.getId() != null) { + ruleNodeIndexMap.put(node.getId(), nodes.indexOf(node)); + } else { + toAddOrUpdate.add(node); + } + } + } + + List existingRuleNodes = getRuleChainNodes(ruleChainMetaData.getRuleChainId()); + for (RuleNode existingNode : existingRuleNodes) { + deleteEntityRelations(existingNode.getId()); + Integer index = ruleNodeIndexMap.get(existingNode.getId()); + if (index != null) { + toAddOrUpdate.add(ruleChainMetaData.getNodes().get(index)); + } else { + toDelete.add(existingNode); + } + } + for (RuleNode node : toAddOrUpdate) { + node.setRuleChainId(ruleChain.getId()); + RuleNode savedNode = ruleNodeDao.save(node); + try { + createRelation(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), + EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create rule chain to rule node relation. from: [{}], to: [{}]", + ruleChainMetaData.getRuleChainId(), savedNode.getId()); + throw new RuntimeException(e); + } + int index = nodes.indexOf(node); + nodes.set(index, savedNode); + ruleNodeIndexMap.put(savedNode.getId(), index); + } + for (RuleNode node : toDelete) { + deleteRuleNode(node.getId()); + } + RuleNodeId firstRuleNodeId = null; + if (ruleChainMetaData.getFirstNodeIndex() != null) { + firstRuleNodeId = nodes.get(ruleChainMetaData.getFirstNodeIndex()).getId(); + } + if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId)) + || (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) { + ruleChain.setFirstRuleNodeId(firstRuleNodeId); + ruleChainDao.save(ruleChain); + } + if (ruleChainMetaData.getConnections() != null) { + for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) { + EntityId from = nodes.get(nodeConnection.getFromIndex()).getId(); + EntityId to = nodes.get(nodeConnection.getToIndex()).getId(); + String type = nodeConnection.getType(); + try { + createRelation(new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create rule node relation. from: [{}], to: [{}]", from, to); + throw new RuntimeException(e); + } + } + } + if (ruleChainMetaData.getRuleChainConnections() != null) { + for (RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) { + EntityId from = nodes.get(nodeToRuleChainConnection.getFromIndex()).getId(); + EntityId to = nodeToRuleChainConnection.getTargetRuleChainId(); + String type = nodeToRuleChainConnection.getType(); + try { + createRelation(new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE, nodeToRuleChainConnection.getAdditionalInfo())); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create rule node to rule chain relation. from: [{}], to: [{}]", from, to); + throw new RuntimeException(e); + } + } + } + + return loadRuleChainMetaData(ruleChainMetaData.getRuleChainId()); + } + + @Override + public RuleChainMetaData loadRuleChainMetaData(RuleChainId ruleChainId) { + Validator.validateId(ruleChainId, "Incorrect rule chain id."); + RuleChain ruleChain = findRuleChainById(ruleChainId); + if (ruleChain == null) { + return null; + } + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); + ruleChainMetaData.setRuleChainId(ruleChainId); + List ruleNodes = getRuleChainNodes(ruleChainId); + Map ruleNodeIndexMap = new HashMap<>(); + for (RuleNode node : ruleNodes) { + ruleNodeIndexMap.put(node.getId(), ruleNodes.indexOf(node)); + } + ruleChainMetaData.setNodes(ruleNodes); + if (ruleChain.getFirstRuleNodeId() != null) { + ruleChainMetaData.setFirstNodeIndex(ruleNodeIndexMap.get(ruleChain.getFirstRuleNodeId())); + } + for (RuleNode node : ruleNodes) { + int fromIndex = ruleNodeIndexMap.get(node.getId()); + List nodeRelations = getRuleNodeRelations(node.getId()); + for (EntityRelation nodeRelation : nodeRelations) { + String type = nodeRelation.getType(); + if (nodeRelation.getTo().getEntityType() == EntityType.RULE_NODE) { + RuleNodeId toNodeId = new RuleNodeId(nodeRelation.getTo().getId()); + int toIndex = ruleNodeIndexMap.get(toNodeId); + ruleChainMetaData.addConnectionInfo(fromIndex, toIndex, type); + } else if (nodeRelation.getTo().getEntityType() == EntityType.RULE_CHAIN) { + RuleChainId targetRuleChainId = new RuleChainId(nodeRelation.getTo().getId()); + ruleChainMetaData.addRuleChainConnectionInfo(fromIndex, targetRuleChainId, type, nodeRelation.getAdditionalInfo()); + } + } + } + return ruleChainMetaData; + } + + @Override + public RuleChain findRuleChainById(RuleChainId ruleChainId) { + Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); + return ruleChainDao.findById(ruleChainId.getId()); + } + + @Override + public RuleNode findRuleNodeById(RuleNodeId ruleNodeId) { + Validator.validateId(ruleNodeId, "Incorrect rule node id for search request."); + return ruleNodeDao.findById(ruleNodeId.getId()); + } + + @Override + public ListenableFuture findRuleChainByIdAsync(RuleChainId ruleChainId) { + Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); + return ruleChainDao.findByIdAsync(ruleChainId.getId()); + } + + @Override + public ListenableFuture findRuleNodeByIdAsync(RuleNodeId ruleNodeId) { + Validator.validateId(ruleNodeId, "Incorrect rule node id for search request."); + return ruleNodeDao.findByIdAsync(ruleNodeId.getId()); + } + + @Override + public RuleChain getRootTenantRuleChain(TenantId tenantId) { + Validator.validateId(tenantId, "Incorrect tenant id for search request."); + List relations = relationService.findByFrom(tenantId, RelationTypeGroup.RULE_CHAIN); + if (relations != null && !relations.isEmpty()) { + EntityRelation relation = relations.get(0); + RuleChainId ruleChainId = new RuleChainId(relation.getTo().getId()); + return findRuleChainById(ruleChainId); + } else { + return null; + } + } + + @Override + public List getRuleChainNodes(RuleChainId ruleChainId) { + Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); + List relations = getRuleChainToNodeRelations(ruleChainId); + List ruleNodes = relations.stream().map(relation -> ruleNodeDao.findById(relation.getTo().getId())).collect(Collectors.toList()); + return ruleNodes; + } + + @Override + public List getRuleNodeRelations(RuleNodeId ruleNodeId) { + Validator.validateId(ruleNodeId, "Incorrect rule node id for search request."); + return relationService.findByFrom(ruleNodeId, RelationTypeGroup.RULE_NODE); + } + + @Override + public TextPageData findTenantRuleChains(TenantId tenantId, TextPageLink pageLink) { + Validator.validateId(tenantId, "Incorrect tenant id for search rule chain request."); + Validator.validatePageLink(pageLink, "Incorrect PageLink object for search rule chain request."); + List ruleChains = ruleChainDao.findRuleChainsByTenantId(tenantId.getId(), pageLink); + return new TextPageData<>(ruleChains, pageLink); + } + + @Override + public void deleteRuleChainById(RuleChainId ruleChainId) { + Validator.validateId(ruleChainId, "Incorrect rule chain id for delete request."); + RuleChain ruleChain = ruleChainDao.findById(ruleChainId.getId()); + if (ruleChain != null && ruleChain.isRoot()) { + throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); + } + checkRuleNodesAndDelete(ruleChainId); + } + + @Override + public void deleteRuleChainsByTenantId(TenantId tenantId) { + Validator.validateId(tenantId, "Incorrect tenant id for delete rule chains request."); + tenantRuleChainsRemover.removeEntities(tenantId); + } + + private void checkRuleNodesAndDelete(RuleChainId ruleChainId) { + List nodeRelations = getRuleChainToNodeRelations(ruleChainId); + for (EntityRelation relation : nodeRelations) { + deleteRuleNode(relation.getTo()); + } + deleteEntityRelations(ruleChainId); + ruleChainDao.removeById(ruleChainId.getId()); + } + + private List getRuleChainToNodeRelations(RuleChainId ruleChainId) { + return relationService.findByFrom(ruleChainId, RelationTypeGroup.RULE_CHAIN); + } + + private void deleteRuleNode(EntityId entityId) { + deleteEntityRelations(entityId); + ruleNodeDao.removeById(entityId.getId()); + } + + private void createRelation(EntityRelation relation) throws ExecutionException, InterruptedException { + log.debug("Creating relation: {}", relation); + relationService.saveRelation(relation); + } + + private void deleteRelation(EntityRelation relation) throws ExecutionException, InterruptedException { + log.debug("Deleting relation: {}", relation); + relationService.deleteRelation(relation); + } + + private DataValidator ruleChainValidator = + new DataValidator() { + @Override + protected void validateDataImpl(RuleChain ruleChain) { + if (StringUtils.isEmpty(ruleChain.getName())) { + throw new DataValidationException("Rule chain name should be specified!."); + } + if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) { + throw new DataValidationException("Rule chain should be assigned to tenant!"); + } + Tenant tenant = tenantDao.findById(ruleChain.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Rule chain is referencing to non-existent tenant!"); + } + if (ruleChain.isRoot()) { + RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); + if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) { + throw new DataValidationException("Another root rule chain is present in scope of current tenant!"); + } + } + } + }; + + private PaginatedRemover tenantRuleChainsRemover = + new PaginatedRemover() { + + @Override + protected List findEntities(TenantId id, TextPageLink pageLink) { + return ruleChainDao.findRuleChainsByTenantId(id.getId(), pageLink); + } + + @Override + protected void removeEntity(RuleChain entity) { + checkRuleNodesAndDelete(entity.getId()); + } + }; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java deleted file mode 100644 index f1df09edb9..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java +++ /dev/null @@ -1,300 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.common.util.concurrent.ListenableFuture; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.ComponentDescriptor; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; -import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.component.ComponentDescriptorService; -import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.DatabaseException; -import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.service.Validator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validatePageLink; - -@Service -@Slf4j -public class BaseRuleService extends AbstractEntityService implements RuleService { - - private final TenantId systemTenantId = new TenantId(NULL_UUID); - - @Autowired - public RuleDao ruleDao; - - @Autowired - public PluginService pluginService; - - @Autowired - private ComponentDescriptorService componentDescriptorService; - - @Override - public RuleMetaData saveRule(RuleMetaData rule) { - ruleValidator.validate(rule); - if (rule.getTenantId() == null) { - log.trace("Save system rule metadata with predefined id {}", systemTenantId); - rule.setTenantId(systemTenantId); - } - if (rule.getId() != null) { - RuleMetaData oldVersion = ruleDao.findById(rule.getId()); - if (rule.getState() == null) { - rule.setState(oldVersion.getState()); - } else if (rule.getState() != oldVersion.getState()) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the rule!"); - } - } else { - if (rule.getState() == null) { - rule.setState(ComponentLifecycleState.SUSPENDED); - } else if (rule.getState() != ComponentLifecycleState.SUSPENDED) { - throw new IncorrectParameterException("Use Activate/Suspend method to control state of the rule!"); - } - } - - validateFilters(rule.getFilters()); - if (rule.getProcessor() != null && !rule.getProcessor().isNull()) { - validateComponentJson(rule.getProcessor(), ComponentType.PROCESSOR); - } - if (rule.getAction() != null && !rule.getAction().isNull()) { - validateComponentJson(rule.getAction(), ComponentType.ACTION); - } - validateRuleAndPluginState(rule); - return ruleDao.save(rule); - } - - private void validateFilters(JsonNode filtersJson) { - if (filtersJson == null || filtersJson.isNull()) { - throw new IncorrectParameterException("Rule filters are required!"); - } - if (!filtersJson.isArray()) { - throw new IncorrectParameterException("Filters json is not an array!"); - } - ArrayNode filtersArray = (ArrayNode) filtersJson; - for (int i = 0; i < filtersArray.size(); i++) { - validateComponentJson(filtersArray.get(i), ComponentType.FILTER); - } - } - - private void validateComponentJson(JsonNode json, ComponentType type) { - if (json == null || json.isNull()) { - throw new IncorrectParameterException(type.name() + " is required!"); - } - String clazz = getIfValid(type.name(), json, "clazz", JsonNode::isTextual, JsonNode::asText); - String name = getIfValid(type.name(), json, "name", JsonNode::isTextual, JsonNode::asText); - JsonNode configuration = getIfValid(type.name(), json, "configuration", JsonNode::isObject, node -> node); - ComponentDescriptor descriptor = componentDescriptorService.findByClazz(clazz); - if (descriptor == null) { - throw new IncorrectParameterException(type.name() + " clazz " + clazz + " is not a valid component!"); - } - if (descriptor.getType() != type) { - throw new IncorrectParameterException("Clazz " + clazz + " is not a valid " + type.name() + " component!"); - } - if (!componentDescriptorService.validate(descriptor, configuration)) { - throw new IncorrectParameterException(type.name() + " configuration is not valid!"); - } - } - - private void validateRuleAndPluginState(RuleMetaData rule) { - if (org.springframework.util.StringUtils.isEmpty(rule.getPluginToken())) { - return; - } - PluginMetaData pluginMd = pluginService.findPluginByApiToken(rule.getPluginToken()); - if (pluginMd == null) { - throw new IncorrectParameterException("Rule points to non-existent plugin!"); - } - if (!pluginMd.getTenantId().equals(systemTenantId) && !pluginMd.getTenantId().equals(rule.getTenantId())) { - throw new IncorrectParameterException("Rule access plugin that belongs to different tenant!"); - } - if (rule.getState() == ComponentLifecycleState.ACTIVE && pluginMd.getState() != ComponentLifecycleState.ACTIVE) { - throw new IncorrectParameterException("Can't save active rule that points to inactive plugin!"); - } - ComponentDescriptor pluginDescriptor = componentDescriptorService.findByClazz(pluginMd.getClazz()); - String actionClazz = getIfValid(ComponentType.ACTION.name(), rule.getAction(), "clazz", JsonNode::isTextual, JsonNode::asText); - if (!Arrays.asList(pluginDescriptor.getActions().split(",")).contains(actionClazz)) { - throw new IncorrectParameterException("Rule's action is not supported by plugin with token " + rule.getPluginToken() + "!"); - } - } - - private static T getIfValid(String parentName, JsonNode node, String name, Function validator, Function extractor) { - if (!node.has(name)) { - throw new IncorrectParameterException(parentName + "'s " + name + " is not set!"); - } else { - JsonNode value = node.get(name); - if (validator.apply(value)) { - return extractor.apply(value); - } else { - throw new IncorrectParameterException(parentName + "'s " + name + " is not valid!"); - } - } - } - - @Override - public RuleMetaData findRuleById(RuleId ruleId) { - validateId(ruleId, "Incorrect rule id for search rule request."); - return ruleDao.findById(ruleId.getId()); - } - - @Override - public ListenableFuture findRuleByIdAsync(RuleId ruleId) { - validateId(ruleId, "Incorrect rule id for search rule request."); - return ruleDao.findByIdAsync(ruleId.getId()); - } - - @Override - public List findPluginRules(String pluginToken) { - return ruleDao.findRulesByPlugin(pluginToken); - } - - @Override - public TextPageData findSystemRules(TextPageLink pageLink) { - validatePageLink(pageLink, "Incorrect PageLink object for search rule request."); - List rules = ruleDao.findByTenantIdAndPageLink(systemTenantId, pageLink); - return new TextPageData<>(rules, pageLink); - } - - @Override - public TextPageData findTenantRules(TenantId tenantId, TextPageLink pageLink) { - validateId(tenantId, "Incorrect tenant id for search rule request."); - validatePageLink(pageLink, "Incorrect PageLink object for search rule request."); - List rules = ruleDao.findByTenantIdAndPageLink(tenantId, pageLink); - return new TextPageData<>(rules, pageLink); - } - - @Override - public List findSystemRules() { - log.trace("Executing findSystemRules"); - List rules = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(300); - TextPageData pageData = null; - do { - pageData = findSystemRules(pageLink); - rules.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - return rules; - } - - @Override - public TextPageData findAllTenantRulesByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.trace("Executing findAllTenantRulesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); - List rules = ruleDao.findAllTenantRulesByTenantId(tenantId.getId(), pageLink); - return new TextPageData<>(rules, pageLink); - } - - @Override - public List findAllTenantRulesByTenantId(TenantId tenantId) { - log.trace("Executing findAllTenantRulesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - List rules = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(300); - TextPageData pageData = null; - do { - pageData = findAllTenantRulesByTenantIdAndPageLink(tenantId, pageLink); - rules.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - return rules; - } - - @Override - public void deleteRuleById(RuleId ruleId) { - validateId(ruleId, "Incorrect rule id for delete rule request."); - deleteEntityRelations(ruleId); - ruleDao.deleteById(ruleId); - } - - @Override - public void activateRuleById(RuleId ruleId) { - updateLifeCycleState(ruleId, ComponentLifecycleState.ACTIVE); - } - - @Override - public void suspendRuleById(RuleId ruleId) { - updateLifeCycleState(ruleId, ComponentLifecycleState.SUSPENDED); - } - - private void updateLifeCycleState(RuleId ruleId, ComponentLifecycleState state) { - Validator.validateId(ruleId, "Incorrect rule id for state change request."); - RuleMetaData rule = ruleDao.findById(ruleId); - if (rule != null) { - rule.setState(state); - validateRuleAndPluginState(rule); - ruleDao.save(rule); - } else { - throw new DatabaseException("Plugin not found!"); - } - } - - @Override - public void deleteRulesByTenantId(TenantId tenantId) { - validateId(tenantId, "Incorrect tenant id for delete rules request."); - tenantRulesRemover.removeEntities(tenantId); - } - - private DataValidator ruleValidator = - new DataValidator() { - @Override - protected void validateDataImpl(RuleMetaData rule) { - if (StringUtils.isEmpty(rule.getName())) { - throw new DataValidationException("Rule name should be specified!."); - } - } - }; - - private PaginatedRemover tenantRulesRemover = - new PaginatedRemover() { - - @Override - protected List findEntities(TenantId id, TextPageLink pageLink) { - return ruleDao.findByTenantIdAndPageLink(id, pageLink); - } - - @Override - protected void removeEntity(RuleMetaData entity) { - ruleDao.deleteById(entity.getUuidId()); - } - }; - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraBaseRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraBaseRuleDao.java deleted file mode 100644 index 2c5b1f75ad..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraBaseRuleDao.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.datastax.driver.core.querybuilder.Select; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.model.nosql.RuleMetaDataEntity; -import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; -import org.thingsboard.server.dao.util.NoSqlDao; - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; - -@Component -@Slf4j -@NoSqlDao -public class CassandraBaseRuleDao extends CassandraAbstractSearchTextDao implements RuleDao { - - public static final String SEARCH_RESULT = "Search result: [{}]"; - - @Override - protected Class getColumnFamilyClass() { - return RuleMetaDataEntity.class; - } - - @Override - protected String getColumnFamilyName() { - return ModelConstants.RULE_COLUMN_FAMILY_NAME; - } - - @Override - public RuleMetaData findById(RuleId ruleId) { - return findById(ruleId.getId()); - } - - @Override - public List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.debug("Try to find rules by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = findPageWithTextSearch(ModelConstants.RULE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, - Arrays.asList(eq(ModelConstants.RULE_TENANT_ID_PROPERTY, tenantId.getId())), pageLink); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public List findAllTenantRulesByTenantId(UUID tenantId, TextPageLink pageLink) { - log.debug("Try to find all tenant rules by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = findPageWithTextSearch(ModelConstants.RULE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, - Arrays.asList(in(ModelConstants.RULE_TENANT_ID_PROPERTY, Arrays.asList(NULL_UUID, tenantId))), - pageLink); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public List findRulesByPlugin(String pluginToken) { - log.debug("Search rules by api token [{}]", pluginToken); - Select select = select().from(ModelConstants.RULE_BY_PLUGIN_TOKEN); - Select.Where query = select.where(); - query.and(eq(ModelConstants.RULE_PLUGIN_TOKEN_PROPERTY, pluginToken)); - return DaoUtil.convertDataList(findListByStatement(query)); - } - - @Override - public void deleteById(UUID id) { - log.debug("Delete rule meta-data entity by id [{}]", id); - boolean result = removeById(id); - log.debug("Delete result: [{}]", result); - } - - @Override - public void deleteById(RuleId ruleId) { - deleteById(ruleId.getId()); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java new file mode 100644 index 0000000000..ff672a78db --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2018 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.rule; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.nosql.RuleChainEntity; +import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.util.NoSqlDao; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY; + +@Component +@Slf4j +@NoSqlDao +public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao implements RuleChainDao { + + @Override + protected Class getColumnFamilyClass() { + return RuleChainEntity.class; + } + + @Override + protected String getColumnFamilyName() { + return RULE_CHAIN_COLUMN_FAMILY_NAME; + } + + @Override + public List findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) { + log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink); + List ruleChainEntities = findPageWithTextSearch(RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + Collections.singletonList(eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)), + pageLink); + + log.trace("Found rule chains [{}] by tenantId [{}] and pageLink [{}]", ruleChainEntities, tenantId, pageLink); + return DaoUtil.convertDataList(ruleChainEntities); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java new file mode 100644 index 0000000000..9e34531a71 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2018 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.rule; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.model.nosql.RuleNodeEntity; +import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.util.NoSqlDao; + +import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_COLUMN_FAMILY_NAME; + +@Component +@Slf4j +@NoSqlDao +public class CassandraRuleNodeDao extends CassandraAbstractSearchTextDao implements RuleNodeDao { + + @Override + protected Class getColumnFamilyClass() { + return RuleNodeEntity.class; + } + + @Override + protected String getColumnFamilyName() { + return RULE_NODE_COLUMN_FAMILY_NAME; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/rule/RuleDao.java rename to dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 8d5a7c0be3..4a7cfaea7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -13,37 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.thingsboard.server.dao.rule; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.rule.RuleMetaData; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.Dao; import java.util.List; import java.util.UUID; -public interface RuleDao extends Dao { - - RuleMetaData save(RuleMetaData rule); - - RuleMetaData findById(RuleId ruleId); - - List findRulesByPlugin(String pluginToken); - - List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink); +/** + * Created by igor on 3/12/18. + */ +public interface RuleChainDao extends Dao { /** - * Find all tenant rules (including system) by tenantId and page link. + * Find rule chains by tenantId and page link. * * @param tenantId the tenantId * @param pageLink the page link - * @return the list of rules objects + * @return the list of rule chain objects */ - List findAllTenantRulesByTenantId(UUID tenantId, TextPageLink pageLink); - - void deleteById(UUID id); + List findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink); - void deleteById(RuleId ruleId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java new file mode 100644 index 0000000000..4dbe6c9de4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2018 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.rule; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; + +import java.util.List; + +/** + * Created by igor on 3/12/18. + */ +public interface RuleChainService { + + RuleChain saveRuleChain(RuleChain ruleChain); + + boolean setRootRuleChain(RuleChainId ruleChainId); + + RuleChainMetaData saveRuleChainMetaData(RuleChainMetaData ruleChainMetaData); + + RuleChainMetaData loadRuleChainMetaData(RuleChainId ruleChainId); + + RuleChain findRuleChainById(RuleChainId ruleChainId); + + RuleNode findRuleNodeById(RuleNodeId ruleNodeId); + + ListenableFuture findRuleChainByIdAsync(RuleChainId ruleChainId); + + ListenableFuture findRuleNodeByIdAsync(RuleNodeId ruleNodeId); + + RuleChain getRootTenantRuleChain(TenantId tenantId); + + List getRuleChainNodes(RuleChainId ruleChainId); + + List getRuleNodeRelations(RuleNodeId ruleNodeId); + + TextPageData findTenantRuleChains(TenantId tenantId, TextPageLink pageLink); + + void deleteRuleChainById(RuleChainId ruleChainId); + + void deleteRuleChainsByTenantId(TenantId tenantId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java new file mode 100644 index 0000000000..7ef4b3e204 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2018 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.rule; + +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.Dao; + +/** + * Created by igor on 3/12/18. + */ +public interface RuleNodeDao extends Dao { + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleService.java deleted file mode 100644 index ce2f2dc07d..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleService.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.rule.RuleMetaData; - -import java.util.List; - -public interface RuleService { - - RuleMetaData saveRule(RuleMetaData device); - - RuleMetaData findRuleById(RuleId ruleId); - - ListenableFuture findRuleByIdAsync(RuleId ruleId); - - List findPluginRules(String pluginToken); - - TextPageData findSystemRules(TextPageLink pageLink); - - TextPageData findTenantRules(TenantId tenantId, TextPageLink pageLink); - - List findSystemRules(); - - TextPageData findAllTenantRulesByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink); - - List findAllTenantRulesByTenantId(TenantId tenantId); - - void activateRuleById(RuleId ruleId); - - void suspendRuleById(RuleId ruleId); - - void deleteRuleById(RuleId ruleId); - - void deleteRulesByTenantId(TenantId tenantId); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java b/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java index c8bb38dc5c..52fcf85c17 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.service; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.page.TimePageLink; -import java.sql.Time; import java.util.List; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index c8af7cb981..1947f4adf0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.service; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.BasePageLink; -import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java b/dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java index 6cd3719688..ebc66d7d9a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/CassandraAdminSettingsDao.java @@ -26,7 +26,9 @@ import org.thingsboard.server.dao.util.NoSqlDao; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_BY_KEY_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ADMIN_SETTINGS_KEY_PROPERTY; @Component @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index 0d64d5c038..ff6e2130f8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.dao.sql.alarm; -import com.google.common.base.Function; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -102,12 +100,12 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; ListenableFuture> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); - return Futures.transform(relations, (AsyncFunction, List>) input -> { + return Futures.transformAsync(relations, input -> { List> alarmFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { alarmFutures.add(Futures.transform( findAlarmByIdAsync(relation.getTo().getId()), - (Function) AlarmInfo::new)); + AlarmInfo::new)); } return Futures.successfulAsList(alarmFutures); }); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index bf1e289e18..7887ea8615 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -19,8 +19,6 @@ 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.EntitySubtype; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.dao.model.sql.AssetEntity; import org.thingsboard.server.dao.util.SqlDao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 9f8f6737cb..fd5817c590 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -31,7 +31,12 @@ import org.thingsboard.server.dao.model.sql.AssetEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java index 6a7d11917e..4c0ff69e27 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java @@ -25,7 +25,6 @@ import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.UUID; /** * Created by Valerii Sosliuk on 5/6/2017. diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index f4813877b8..ee55038e46 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -23,7 +23,6 @@ import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.UUID; /** * Created by Valerii Sosliuk on 5/6/2017. diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index 4d8d0b2ff6..7f40741d47 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.dashboard; -import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -38,7 +37,6 @@ import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; -import java.sql.Time; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -86,7 +84,7 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); - return Futures.transform(relations, (AsyncFunction, List>) input -> { + return Futures.transformAsync(relations, input -> { List> dashboardFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { dashboardFutures.add(findByIdAsync(relation.getTo().getId())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 4f3cd7d6fb..e464c65e8e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -32,7 +32,12 @@ import org.thingsboard.server.dao.model.sql.DeviceEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java index 89cd9444d7..07fb5a0e89 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java @@ -15,12 +15,17 @@ */ package org.thingsboard.server.dao.sql.event; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +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.EntityType; import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.util.SqlDao; +import java.util.List; + /** * Created by Valerii Sosliuk on 5/3/2017. */ @@ -36,4 +41,14 @@ public interface EventRepository extends CrudRepository, Jp EventEntity findByTenantIdAndEntityTypeAndEntityId(String tenantId, EntityType entityType, String entityId); + + @Query("SELECT e FROM EventEntity e WHERE e.tenantId = :tenantId AND e.entityType = :entityType " + + "AND e.entityId = :entityId AND e.eventType = :eventType ORDER BY e.eventType DESC, e.id DESC") + List findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( + @Param("tenantId") String tenantId, + @Param("entityType") EntityType entityType, + @Param("entityId") String entityId, + @Param("eventType") String eventType, + Pageable pageable); + } 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 9b22773e1e..5a63ed8a46 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 @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.sql.event; import com.datastax.driver.core.utils.UUIDs; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -36,10 +37,7 @@ import org.thingsboard.server.dao.model.sql.EventEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; import org.thingsboard.server.dao.util.SqlDao; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -84,6 +82,18 @@ public class JpaBaseEventDao extends JpaAbstractSearchTimeDao saveAsync(Event event) { + log.debug("Save event [{}] ", event); + if (event.getId() == null) { + event.setId(new EventId(UUIDs.timeBased())); + } + if (StringUtils.isEmpty(event.getUid())) { + event.setUid(event.getId().toString()); + } + return service.submit(() -> save(new EventEntity(event), false).orElse(null)); + } + @Override public Optional saveIfNotExists(Event event) { return save(new EventEntity(event), true); @@ -92,7 +102,7 @@ public class JpaBaseEventDao extends JpaAbstractSearchTimeDao findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) { + List latest = eventRepository.findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( + UUIDConverter.fromTimeUUID(tenantId), + entityId.getEntityType(), + UUIDConverter.fromTimeUUID(entityId.getId()), + eventType, + new PageRequest(0, limit)); + return DaoUtil.convertDataList(latest); + } + 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/sql/plugin/JpaBasePluginDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/plugin/JpaBasePluginDao.java deleted file mode 100644 index 7b7631460d..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/plugin/JpaBasePluginDao.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.UUIDConverter; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.PluginMetaDataEntity; -import org.thingsboard.server.dao.plugin.PluginDao; -import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; - -/** - * Created by Valerii Sosliuk on 5/1/2017. - */ -@Slf4j -@Component -@SqlDao -public class JpaBasePluginDao extends JpaAbstractSearchTextDao implements PluginDao { - - public static final String SEARCH_RESULT = "Search result: [{}]"; - @Autowired - private PluginMetaDataRepository pluginMetaDataRepository; - - @Override - protected Class getEntityClass() { - return PluginMetaDataEntity.class; - } - - @Override - protected CrudRepository getCrudRepository() { - return pluginMetaDataRepository; - } - - @Override - public PluginMetaData findById(PluginId pluginId) { - log.debug("Search plugin meta-data entity by id [{}]", pluginId); - PluginMetaData pluginMetaData = super.findById(pluginId.getId()); - if (log.isTraceEnabled()) { - log.trace("Search result: [{}] for plugin entity [{}]", pluginMetaData != null, pluginMetaData); - } else { - log.debug(SEARCH_RESULT, pluginMetaData != null); - } - return pluginMetaData; - } - - @Override - public PluginMetaData findByApiToken(String apiToken) { - log.debug("Search plugin meta-data entity by api token [{}]", apiToken); - PluginMetaDataEntity entity = pluginMetaDataRepository.findByApiToken(apiToken); - if (log.isTraceEnabled()) { - log.trace("Search result: [{}] for plugin entity [{}]", entity != null, entity); - } else { - log.debug(SEARCH_RESULT, entity != null); - } - return DaoUtil.getData(entity); - } - - @Override - public void deleteById(UUID id) { - log.debug("Delete plugin meta-data entity by id [{}]", id); - pluginMetaDataRepository.delete(UUIDConverter. fromTimeUUID(id)); - } - - @Override - public void deleteById(PluginId pluginId) { - deleteById(pluginId.getId()); - } - - @Override - public List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.debug("Try to find plugins by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = pluginMetaDataRepository - .findByTenantIdAndPageLink( - UUIDConverter.fromTimeUUID(tenantId.getId()), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), - new PageRequest(0, pageLink.getLimit())); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public List findAllTenantPluginsByTenantId(UUID tenantId, TextPageLink pageLink) { - log.debug("Try to find all tenant plugins by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = pluginMetaDataRepository - .findAllTenantPluginsByTenantId( - UUIDConverter.fromTimeUUID(tenantId), - NULL_UUID_STR, - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), - new PageRequest(0, pageLink.getLimit())); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/plugin/PluginMetaDataRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/plugin/PluginMetaDataRepository.java deleted file mode 100644 index 614dfda363..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/plugin/PluginMetaDataRepository.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -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.dao.model.sql.PluginMetaDataEntity; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; - -/** - * Created by Valerii Sosliuk on 5/1/2017. - */ -@SqlDao -public interface PluginMetaDataRepository extends CrudRepository { - - PluginMetaDataEntity findByApiToken(String apiToken); - - @Query("SELECT pmd FROM PluginMetaDataEntity pmd WHERE pmd.tenantId = :tenantId " + - "AND LOWER(pmd.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND pmd.id > :idOffset ORDER BY pmd.id") - List findByTenantIdAndPageLink(@Param("tenantId") String tenantId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); - - @Query("SELECT pmd FROM PluginMetaDataEntity pmd WHERE pmd.tenantId IN (:tenantId, :nullTenantId) " + - "AND LOWER(pmd.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND pmd.id > :idOffset ORDER BY pmd.id") - List findAllTenantPluginsByTenantId(@Param("tenantId") String tenantId, - @Param("nullTenantId") String nullTenantId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index 6776f562bc..a8de6775e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; @@ -38,14 +37,10 @@ import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; import org.thingsboard.server.dao.util.SqlDao; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; -import static org.springframework.data.domain.Sort.Direction.ASC; import static org.springframework.data.jpa.domain.Specifications.where; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -132,39 +127,35 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple @Override public boolean deleteRelation(EntityRelation relation) { RelationCompositeKey key = new RelationCompositeKey(relation); - boolean relationExistsBeforeDelete = relationRepository.exists(key); - relationRepository.delete(key); - return relationExistsBeforeDelete; + return deleteRelationIfExists(key); } @Override public ListenableFuture deleteRelationAsync(EntityRelation relation) { RelationCompositeKey key = new RelationCompositeKey(relation); return service.submit( - () -> { - boolean relationExistsBeforeDelete = relationRepository.exists(key); - relationRepository.delete(key); - return relationExistsBeforeDelete; - }); + () -> deleteRelationIfExists(key)); } @Override public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - boolean relationExistsBeforeDelete = relationRepository.exists(key); - relationRepository.delete(key); - return relationExistsBeforeDelete; + return deleteRelationIfExists(key); } @Override public ListenableFuture deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); return service.submit( - () -> { - boolean relationExistsBeforeDelete = relationRepository.exists(key); - relationRepository.delete(key); - return relationExistsBeforeDelete; - }); + () -> deleteRelationIfExists(key)); + } + + private boolean deleteRelationIfExists(RelationCompositeKey key) { + boolean relationExistsBeforeDelete = relationRepository.exists(key); + if (relationExistsBeforeDelete) { + relationRepository.delete(key); + } + return relationExistsBeforeDelete; } @Override @@ -172,7 +163,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple boolean relationExistsBeforeDelete = relationRepository .findAllByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()) .size() > 0; - relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); + if (relationExistsBeforeDelete) { + relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); + } return relationExistsBeforeDelete; } @@ -183,7 +176,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple boolean relationExistsBeforeDelete = relationRepository .findAllByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()) .size() > 0; - relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); + if (relationExistsBeforeDelete) { + relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); + } return relationExistsBeforeDelete; }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDao.java deleted file mode 100644 index 5ff8298b48..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDao.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.UUIDConverter; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.RuleMetaDataEntity; -import org.thingsboard.server.dao.rule.RuleDao; -import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; - -/** - * Created by Valerii Sosliuk on 4/30/2017. - */ -@Slf4j -@Component -@SqlDao -public class JpaBaseRuleDao extends JpaAbstractSearchTextDao implements RuleDao { - - public static final String SEARCH_RESULT = "Search result: [{}]"; - @Autowired - private RuleMetaDataRepository ruleMetaDataRepository; - - @Override - protected Class getEntityClass() { - return RuleMetaDataEntity.class; - } - - @Override - protected CrudRepository getCrudRepository() { - return ruleMetaDataRepository; - } - - @Override - public RuleMetaData findById(RuleId ruleId) { - return findById(ruleId.getId()); - } - - @Override - public List findRulesByPlugin(String pluginToken) { - log.debug("Search rules by api token [{}]", pluginToken); - return DaoUtil.convertDataList(ruleMetaDataRepository.findByPluginToken(pluginToken)); - } - - @Override - public List findByTenantIdAndPageLink(TenantId tenantId, TextPageLink pageLink) { - log.debug("Try to find rules by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = - ruleMetaDataRepository - .findByTenantIdAndPageLink( - UUIDConverter.fromTimeUUID(tenantId.getId()), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), - new PageRequest(0, pageLink.getLimit())); - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public List findAllTenantRulesByTenantId(UUID tenantId, TextPageLink pageLink) { - log.debug("Try to find all tenant rules by tenantId [{}] and pageLink [{}]", tenantId, pageLink); - List entities = - ruleMetaDataRepository - .findAllTenantRulesByTenantId( - UUIDConverter.fromTimeUUID(tenantId), - NULL_UUID_STR, - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), - new PageRequest(0, pageLink.getLimit())); - - if (log.isTraceEnabled()) { - log.trace(SEARCH_RESULT, Arrays.toString(entities.toArray())); - } else { - log.debug(SEARCH_RESULT, entities.size()); - } - return DaoUtil.convertDataList(entities); - } - - @Override - public void deleteById(UUID id) { - log.debug("Delete rule meta-data entity by id [{}]", id); - ruleMetaDataRepository.delete(UUIDConverter.fromTimeUUID(id)); - } - - @Override - public void deleteById(RuleId ruleId) { - deleteById(ruleId.getId()); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java new file mode 100644 index 0000000000..fb5d20a702 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2018 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.rule; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.RuleChainEntity; +import org.thingsboard.server.dao.rule.RuleChainDao; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; + +@Slf4j +@Component +@SqlDao +public class JpaRuleChainDao extends JpaAbstractSearchTextDao implements RuleChainDao { + + @Autowired + private RuleChainRepository ruleChainRepository; + + @Override + protected Class getEntityClass() { + return RuleChainEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return ruleChainRepository; + } + + @Override + public List findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) { + return DaoUtil.convertDataList(ruleChainRepository + .findByTenantId( + UUIDConverter.fromTimeUUID(tenantId), + Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()))); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java new file mode 100644 index 0000000000..eec28946a8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2018 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.rule; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.model.sql.RuleNodeEntity; +import org.thingsboard.server.dao.rule.RuleNodeDao; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; +import org.thingsboard.server.dao.util.SqlDao; + +@Slf4j +@Component +@SqlDao +public class JpaRuleNodeDao extends JpaAbstractSearchTextDao implements RuleNodeDao { + + @Autowired + private RuleNodeRepository ruleNodeRepository; + + @Override + protected Class getEntityClass() { + return RuleNodeEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return ruleNodeRepository; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java new file mode 100644 index 0000000000..58d4aac70b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2018 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.rule; + +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.dao.model.sql.RuleChainEntity; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; + +@SqlDao +public interface RuleChainRepository extends CrudRepository { + + @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + + "AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + + "AND rc.id > :idOffset ORDER BY rc.id") + List findByTenantId(@Param("tenantId") String tenantId, + @Param("searchText") String searchText, + @Param("idOffset") String idOffset, + Pageable pageable); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleMetaDataRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleMetaDataRepository.java deleted file mode 100644 index c2c0e016a0..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleMetaDataRepository.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -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.dao.model.sql.RuleMetaDataEntity; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; - -/** - * Created by Valerii Sosliuk on 4/30/2017. - */ -@SqlDao -public interface RuleMetaDataRepository extends CrudRepository { - - List findByPluginToken(String pluginToken); - - @Query("SELECT rmd FROM RuleMetaDataEntity rmd WHERE rmd.tenantId = :tenantId " + - "AND LOWER(rmd.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND rmd.id > :idOffset ORDER BY rmd.id") - List findByTenantIdAndPageLink(@Param("tenantId") String tenantId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); - - @Query("SELECT rmd FROM RuleMetaDataEntity rmd WHERE rmd.tenantId IN (:tenantId, :nullTenantId) " + - "AND LOWER(rmd.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + - "AND rmd.id > :idOffset ORDER BY rmd.id") - List findAllTenantRulesByTenantId(@Param("tenantId") String tenantId, - @Param("nullTenantId") String nullTenantId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); -} diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java similarity index 67% rename from application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java index 291a9a7cef..574aedf118 100644 --- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleProcessingMsg.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java @@ -13,19 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors.rule; +package org.thingsboard.server.dao.sql.rule; -public class RuleProcessingMsg { +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.RuleNodeEntity; +import org.thingsboard.server.dao.util.SqlDao; - private final ChainProcessingContext ctx; - - public RuleProcessingMsg(ChainProcessingContext ctx) { - super(); - this.ctx = ctx; - } - - public ChainProcessingContext getCtx() { - return ctx; - } +@SqlDao +public interface RuleNodeRepository extends CrudRepository { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 6350352961..e5f145f0fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -17,7 +17,11 @@ package org.thingsboard.server.dao.sql.timeseries; import com.google.common.base.Function; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.*; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -25,7 +29,11 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvQuery; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.TsKvEntity; import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; @@ -42,7 +50,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvLatestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvLatestRepository.java index c5e886125c..7171cef990 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvLatestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/TsKvLatestRepository.java @@ -22,7 +22,6 @@ import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.UUID; @SqlDao public interface TsKvLatestRepository extends CrudRepository { diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java index fc4440396b..1c3a7797b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/CassandraTenantDao.java @@ -28,7 +28,9 @@ import java.util.Arrays; import java.util.List; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_BY_REGION_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_REGION_PROPERTY; @Component @Slf4j 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 24abe52802..d92c9410e5 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 @@ -30,8 +30,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.plugin.PluginService; -import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -71,10 +70,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe private DashboardService dashboardService; @Autowired - private RuleService ruleService; - - @Autowired - private PluginService pluginService; + private RuleChainService ruleChainService; @Override public Tenant findTenantById(TenantId tenantId) { @@ -108,8 +104,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe assetService.deleteAssetsByTenantId(tenantId); deviceService.deleteDevicesByTenantId(tenantId); userService.deleteTenantAdmins(tenantId); - ruleService.deleteRulesByTenantId(tenantId); - pluginService.deletePluginsByTenantId(tenantId); + ruleChainService.deleteRuleChainsByTenantId(tenantId); tenantDao.removeById(tenantId.getId()); deleteEntityRelations(tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java index 99a2bf1e16..ac5ee640c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java @@ -18,7 +18,14 @@ package org.thingsboard.server.dao.timeseries; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; import javax.annotation.Nullable; import java.util.List; 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 cda4b1669b..7aa317cbf9 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 @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.timeseries; -import com.datastax.driver.core.*; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Row; import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.Select; import com.google.common.base.Function; @@ -29,8 +33,17 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.kv.TsKvQuery; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; import org.thingsboard.server.dao.util.NoSqlDao; @@ -69,6 +82,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @Value("${cassandra.query.ts_key_value_partitioning}") private String partitioning; + @Value("${cassandra.query.ts_key_value_ttl}") + private long systemTtl; + private TsPartitionDate tsFormat; private PreparedStatement partitionInsertStmt; @@ -217,7 +233,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem ListenableFuture> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); - ListenableFuture> aggregationChunks = Futures.transform(partitionsListFuture, + ListenableFuture> aggregationChunks = Futures.transformAsync(partitionsListFuture, getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); return Futures.transform(aggregationChunks, new AggregatePartitionsFunction(aggregation, key, ts), readResultsProcessingExecutor); @@ -274,6 +290,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @Override public ListenableFuture save(EntityId entityId, TsKvEntry tsKvEntry, long ttl) { + ttl = computeTtl(ttl); long partition = toPartitionTs(tsKvEntry.getTs()); DataType type = tsKvEntry.getDataType(); BoundStatement stmt = (ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind(); @@ -291,6 +308,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @Override public ListenableFuture savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) { + ttl = computeTtl(ttl); long partition = toPartitionTs(tsKvEntryTs); log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key); BoundStatement stmt = (ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt()).bind(); @@ -304,6 +322,17 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem return getFuture(executeAsyncWrite(stmt), rs -> null); } + private long computeTtl(long ttl) { + if (systemTtl > 0) { + if (ttl == 0) { + ttl = systemTtl; + } else { + ttl = Math.min(systemTtl, ttl); + } + } + return ttl; + } + @Override public ListenableFuture saveLatest(EntityId entityId, TsKvEntry tsKvEntry) { BoundStatement stmt = getLatestStmt().bind() diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 350bf4f7cc..e1bd4e0c65 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -42,7 +42,9 @@ import org.thingsboard.server.dao.tenant.TenantDao; import java.util.List; -import static org.thingsboard.server.dao.service.Validator.*; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validateString; @Service @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateLimiter.java b/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateLimiter.java index 03eb46f1ab..817845b54a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateLimiter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/BufferedRateLimiter.java @@ -25,7 +25,11 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.server.dao.exception.BufferLimitException; -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @Component @@ -125,6 +129,9 @@ public class BufferedRateLimiter implements AsyncRateLimiter { if(permits.get() < permitsLimit) { reprocessQueue(); } + if(permits.get() < permitsLimit) { + reprocessQueue(); + } return lockedFuture.future; } catch (InterruptedException e) { return Futures.immediateFailedFuture(new BufferLimitException()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetTypeDao.java index 43049f90b0..e31809f64a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetTypeDao.java @@ -29,7 +29,11 @@ import java.util.UUID; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_BY_TENANT_AND_ALIASES_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_TENANT_ID_PROPERTY; @Component @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetsBundleDao.java index b5d0e5fe6d..f938a98a54 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/CassandraWidgetsBundleDao.java @@ -29,8 +29,15 @@ import java.util.Arrays; import java.util.List; import java.util.UUID; -import static com.datastax.driver.core.querybuilder.QueryBuilder.*; -import static org.thingsboard.server.dao.model.ModelConstants.*; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.in; +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_ALIAS_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_BY_TENANT_AND_ALIAS_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_TENANT_ID_PROPERTY; @Component @Slf4j diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql index fc557a9869..f03122ab6b 100644 --- a/dao/src/main/resources/cassandra/schema.cql +++ b/dao/src/main/resources/cassandra/schema.cql @@ -428,7 +428,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.attributes_kv_cf ( CREATE TABLE IF NOT EXISTS thingsboard.component_descriptor ( id timeuuid, - type text, //("FILTER", "PROCESSOR", "ACTION", "PLUGIN") + type text, scope text, name text, search_text text, @@ -459,67 +459,12 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.component_desc_by_id AS PRIMARY KEY ( id, clazz, scope, type ) WITH CLUSTERING ORDER BY ( clazz ASC, scope ASC, type DESC); -CREATE TABLE IF NOT EXISTS thingsboard.rule ( - id timeuuid, - tenant_id timeuuid, - name text, - state text, - search_text text, - weight int, - plugin_token text, - filters text, // Format: {"clazz":"A", "name": "Filter A", "configuration": {"types":["TELEMETRY"]}} - processor text, // Format: {"clazz":"A", "name": "Processor A", "configuration": null} - action text, // Format: {"clazz":"A", "name": "Action A", "configuration": null} - additional_info text, - PRIMARY KEY (id, tenant_id) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_by_plugin_token AS - SELECT * - - FROM thingsboard.rule - WHERE tenant_id IS NOT NULL AND id IS NOT NULL AND plugin_token IS NOT NULL - PRIMARY KEY (plugin_token, tenant_id, id) WITH CLUSTERING ORDER BY (tenant_id DESC, id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_by_tenant_and_search_text AS - SELECT * - FROM thingsboard.rule - WHERE tenant_id IS NOT NULL AND id IS NOT NULL AND search_text IS NOT NULL - PRIMARY KEY (tenant_id, search_text, id) WITH CLUSTERING ORDER BY (search_text ASC); - -CREATE TABLE IF NOT EXISTS thingsboard.plugin ( - id uuid, - tenant_id uuid, - name text, - state text, - search_text text, - api_token text, - plugin_class text, - public_access boolean, - configuration text, - additional_info text, - PRIMARY KEY (id, tenant_id) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.plugin_by_api_token AS - SELECT * - FROM thingsboard.plugin - WHERE api_token IS NOT NULL AND id IS NOT NULL AND tenant_id IS NOT NULL - PRIMARY KEY (api_token, id, tenant_id) WITH CLUSTERING ORDER BY (id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.plugin_by_tenant_and_search_text AS - SELECT * - from thingsboard.plugin - WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); - CREATE TABLE IF NOT EXISTS thingsboard.event ( tenant_id timeuuid, // tenant or system id timeuuid, event_type text, event_uid text, - entity_type text, // (device, customer, rule, plugin) + entity_type text, entity_id timeuuid, body text, PRIMARY KEY ((tenant_id, entity_type, entity_id), event_type, event_uid) @@ -542,7 +487,6 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.event_by_id AS PRIMARY KEY ((tenant_id, entity_type, entity_id), id, event_type, event_uid) WITH CLUSTERING ORDER BY (id ASC, event_type ASC, event_uid ASC); - CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id ( tenant_id timeuuid, id timeuuid, @@ -591,8 +535,6 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id ( PRIMARY KEY ((tenant_id, user_id), id) ); - - CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id ( tenant_id timeuuid, id timeuuid, @@ -615,4 +557,84 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( partition bigint, PRIMARY KEY (( tenant_id ), partition) ) WITH CLUSTERING ORDER BY ( partition ASC ) -AND compaction = { 'class' : 'LeveledCompactionStrategy' }; \ No newline at end of file +AND compaction = { 'class' : 'LeveledCompactionStrategy' }; + +CREATE TABLE IF NOT EXISTS thingsboard.msg_queue ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + ts bigint, + msg blob, + PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts)) +WITH CLUSTERING ORDER BY (ts DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + msg_id timeuuid, + PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id)) +WITH CLUSTERING ORDER BY (msg_id DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions ( + node_id timeuuid, + cluster_partition bigint, + ts_partition bigint, + PRIMARY KEY ((node_id, cluster_partition), ts_partition)) +WITH CLUSTERING ORDER BY (ts_partition DESC) +AND compaction = { + 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', + 'min_threshold': '5', + 'base_time_seconds': '43200', + 'max_window_size_seconds': '43200', + 'tombstone_threshold': '0.9', + 'unchecked_tombstone_compaction': 'true' +}; + +CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( + id uuid, + tenant_id uuid, + name text, + search_text text, + first_rule_node_id uuid, + root boolean, + debug_mode boolean, + configuration text, + additional_info text, + PRIMARY KEY (id, tenant_id) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS + SELECT * + from thingsboard.rule_chain + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id ) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); + +CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( + id uuid, + rule_chain_id uuid, + type text, + name text, + debug_mode boolean, + search_text text, + configuration text, + additional_info text, + PRIMARY KEY (id) +); diff --git a/dao/src/main/resources/cassandra/system-data.cql b/dao/src/main/resources/cassandra/system-data.cql index 2455ac6f5d..16a53cad5c 100644 --- a/dao/src/main/resources/cassandra/system-data.cql +++ b/dao/src/main/resources/cassandra/system-data.cql @@ -279,28 +279,4 @@ VALUES ( now ( ), minTimeuuid ( 0 ), 'charts', 'basic_timeseries', '{"type":"timeseries","sizeX":8,"sizeY":5,"resources":[],"templateHtml":"","templateCss":".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n","controllerScript":"self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n","settingsSchema":"{}","dataKeySettingsSchema":"{}","defaultConfig":"{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Timeseries - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"}', 'Timeseries - Flot' ); - -/** System plugins and rules **/ -INSERT INTO thingsboard.plugin ( id, tenant_id, name, state, search_text, api_token, plugin_class, public_access, -configuration ) -VALUES ( minTimeuuid ( '2016-11-01 01:01:01+0000' ), minTimeuuid ( 0 ), 'System Telemetry Plugin', 'ACTIVE', -'system telemetry plugin', 'telemetry', -'org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin', true, '{}' ); - -INSERT INTO thingsboard.rule ( id, tenant_id, name, plugin_token, state, search_text, weight, filters, processor, -action ) -VALUES ( minTimeuuid ( '2016-11-01 01:01:02+0000' ), minTimeuuid ( 0 ), 'System Telemetry Rule', 'telemetry', 'ACTIVE', -'system telemetry rule', 0, -'[{"clazz":"org.thingsboard.server.extensions.core.filter.MsgTypeFilter", "name":"TelemetryFilter", "configuration": {"messageTypes":["POST_TELEMETRY","POST_ATTRIBUTES","GET_ATTRIBUTES"]}}]', -null, -'{"clazz":"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction", "name":"TelemetryMsgConverterAction", "configuration":{}}' -); - -INSERT INTO thingsboard.plugin ( id, tenant_id, name, state, search_text, api_token, plugin_class, public_access, -configuration ) -VALUES ( minTimeuuid ( '2016-11-01 01:01:03+0000' ), minTimeuuid ( 0 ), 'System RPC Plugin', 'ACTIVE', -'system rpc plugin', 'rpc', 'org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin', true, '{ - "defaultTimeout": 20000 - }' ); - /** SYSTEM **/ diff --git a/dao/src/main/resources/sql/schema.sql b/dao/src/main/resources/sql/schema.sql index 9f03dc8a1b..91e77da503 100644 --- a/dao/src/main/resources/sql/schema.sql +++ b/dao/src/main/resources/sql/schema.sql @@ -140,19 +140,6 @@ CREATE TABLE IF NOT EXISTS event ( CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid) ); -CREATE TABLE IF NOT EXISTS plugin ( - id varchar(31) NOT NULL CONSTRAINT plugin_pkey PRIMARY KEY, - additional_info varchar, - api_token varchar(255), - plugin_class varchar(255), - configuration varchar, - name varchar(255), - public_access boolean, - search_text varchar(255), - state varchar(255), - tenant_id varchar(31) -); - CREATE TABLE IF NOT EXISTS relation ( from_id varchar(31), from_type varchar(255), @@ -164,20 +151,6 @@ CREATE TABLE IF NOT EXISTS relation ( CONSTRAINT relation_unq_key UNIQUE (from_id, from_type, relation_type_group, relation_type, to_id, to_type) ); -CREATE TABLE IF NOT EXISTS rule ( - id varchar(31) NOT NULL CONSTRAINT rule_pkey PRIMARY KEY, - action varchar, - additional_info varchar, - filters varchar, - name varchar(255), - plugin_token varchar(255), - processor varchar, - search_text varchar(255), - state varchar(255), - tenant_id varchar(31), - weight integer -); - CREATE TABLE IF NOT EXISTS tb_user ( id varchar(31) NOT NULL CONSTRAINT tb_user_pkey PRIMARY KEY, additional_info varchar, @@ -254,4 +227,27 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( search_text varchar(255), tenant_id varchar(31), title varchar(255) -); \ No newline at end of file +); + +CREATE TABLE IF NOT EXISTS rule_chain ( + id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, + additional_info varchar, + configuration varchar(10000000), + name varchar(255), + first_rule_node_id varchar(31), + root boolean, + debug_mode boolean, + search_text varchar(255), + tenant_id varchar(31) +); + +CREATE TABLE IF NOT EXISTS rule_node ( + id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, + rule_chain_id varchar(31), + additional_info varchar, + configuration varchar(10000000), + type varchar(255), + name varchar(255), + debug_mode boolean, + search_text varchar(255) +); diff --git a/dao/src/main/resources/sql/system-data.sql b/dao/src/main/resources/sql/system-data.sql index 39fd6bd2f2..d41521a0b7 100644 --- a/dao/src/main/resources/sql/system-data.sql +++ b/dao/src/main/resources/sql/system-data.sql @@ -42,23 +42,3 @@ VALUES ( '1e746126eaaefa6a91992ebcb67fe33', 'mail', '{ "username": "", "password": "" }' ); - -/** System plugins and rules **/ -INSERT INTO plugin ( id, tenant_id, name, state, search_text, api_token, plugin_class, public_access, configuration ) -VALUES ( '1e7461160cb2da2a91992ebcb67fe33', '1b21dd2138140008080808080808080', 'System Telemetry Plugin', 'ACTIVE', - 'system telemetry plugin', 'telemetry', - 'org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin', true, '{}' ); - -INSERT INTO rule ( id, tenant_id, name, plugin_token, state, search_text, weight, filters, processor, action ) -VALUES ( '1e7461165abad4ca91992ebcb67fe33', '1b21dd2138140008080808080808080', 'System Telemetry Rule', 'telemetry', 'ACTIVE', - 'system telemetry rule', 0, - '[{"clazz":"org.thingsboard.server.extensions.core.filter.MsgTypeFilter", "name":"TelemetryFilter", "configuration": {"messageTypes":["POST_TELEMETRY","POST_ATTRIBUTES","GET_ATTRIBUTES"]}}]', - null, - '{"clazz":"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction", "name":"TelemetryMsgConverterAction", "configuration":{}}' -); - -INSERT INTO plugin ( id, tenant_id, name, state, search_text, api_token, plugin_class, public_access, configuration ) -VALUES ( '1e746116b3b8994a91992ebcb67fe33', '1b21dd2138140008080808080808080', 'System RPC Plugin', 'ACTIVE', - 'system rpc plugin', 'rpc', 'org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin', true, '{ - "defaultTimeout": 20000 - }' ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java index d214a70f65..1dc3faa426 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java +++ b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java @@ -15,18 +15,17 @@ */ package org.thingsboard.server.dao; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; import org.cassandraunit.BaseCassandraUnit; import org.cassandraunit.CQLDataLoader; import org.cassandraunit.dataset.CQLDataSet; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Session; - import java.util.List; public class CustomCassandraCQLUnit extends BaseCassandraUnit { - private List dataSets; + protected List dataSets; public Session session; public Cluster cluster; diff --git a/dao/src/test/java/org/thingsboard/server/dao/JpaDbunitTestConfig.java b/dao/src/test/java/org/thingsboard/server/dao/JpaDbunitTestConfig.java index d83293c701..0bf9f19b11 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/JpaDbunitTestConfig.java +++ b/dao/src/test/java/org/thingsboard/server/dao/JpaDbunitTestConfig.java @@ -19,7 +19,6 @@ import com.github.springtestdbunit.bean.DatabaseConfigBean; import com.github.springtestdbunit.bean.DatabaseDataSourceConnectionFactoryBean; import org.dbunit.DatabaseUnitException; import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory; -import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java index 7e25baa45e..f10462d77f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao; import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.extensions.cpsuite.ClasspathSuite; import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters; import org.junit.runner.RunWith; @@ -26,7 +25,9 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.*ServiceNoSqlTest" + "org.thingsboard.server.dao.service.*ServiceNoSqlTest", + "org.thingsboard.server.dao.service.queue.cassandra.*.*.*Test", + "org.thingsboard.server.dao.service.queue.cassandra.*Test" }) public class NoSqlDaoServiceTestSuite { diff --git a/dao/src/test/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFutureTest.java b/dao/src/test/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFutureTest.java index f49668d3fd..491409aac8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFutureTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/nosql/RateLimitedResultSetFutureTest.java @@ -15,7 +15,12 @@ */ package org.thingsboard.server.dao.nosql; -import com.datastax.driver.core.*; +import com.datastax.driver.core.ProtocolVersion; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.Statement; import com.datastax.driver.core.exceptions.UnsupportedFeatureException; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -29,10 +34,18 @@ import org.mockito.stubbing.Answer; import org.thingsboard.server.dao.exception.BufferLimitException; import org.thingsboard.server.dao.util.AsyncRateLimiter; -import java.util.concurrent.*; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class RateLimitedResultSetFutureTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index ffa9f0e7f5..1e1f15daff 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -18,8 +18,6 @@ package org.thingsboard.server.dao.service; import com.datastax.driver.core.utils.UUIDs; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -38,8 +36,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.audit.AuditLogLevelFilter; @@ -50,9 +46,8 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.plugin.PluginService; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.rule.RuleService; +import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -64,8 +59,6 @@ import java.io.IOException; import java.util.Comparator; import java.util.HashMap; import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; @RunWith(SpringRunner.class) @@ -110,12 +103,6 @@ public abstract class AbstractServiceTest { @Autowired protected TimeseriesService tsService; - @Autowired - protected PluginService pluginService; - - @Autowired - protected RuleService ruleService; - @Autowired protected EventService eventService; @@ -125,6 +112,9 @@ public abstract class AbstractServiceTest { @Autowired protected AlarmService alarmService; + @Autowired + protected RuleChainService ruleChainService; + @Autowired private ComponentDescriptorService componentDescriptorService; @@ -149,32 +139,6 @@ public abstract class AbstractServiceTest { return event; } - protected PluginMetaData generatePlugin(TenantId tenantId, String token) throws IOException { - return generatePlugin(tenantId, token, "org.thingsboard.component.PluginTest", "org.thingsboard.component.ActionTest", "TestJsonDescriptor.json", "TestJsonData.json"); - } - - protected PluginMetaData generatePlugin(TenantId tenantId, String token, String clazz, String actions, String configurationDescriptorResource, String dataResource) throws IOException { - if (tenantId == null) { - tenantId = new TenantId(UUIDs.timeBased()); - } - if (token == null) { - token = UUID.randomUUID().toString(); - } - getOrCreateDescriptor(ComponentScope.TENANT, ComponentType.PLUGIN, clazz, configurationDescriptorResource, actions); - PluginMetaData pluginMetaData = new PluginMetaData(); - pluginMetaData.setName("Testing"); - pluginMetaData.setClazz(clazz); - pluginMetaData.setTenantId(tenantId); - pluginMetaData.setApiToken(token); - pluginMetaData.setAdditionalInfo(mapper.readTree("{\"test\":\"test\"}")); - try { - pluginMetaData.setConfiguration(readFromResource(dataResource)); - } catch (IOException e) { - throw new RuntimeException(e); - } - return pluginMetaData; - } - private ComponentDescriptor getOrCreateDescriptor(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptorResource) throws IOException { return getOrCreateDescriptor(scope, type, clazz, configurationDescriptorResource, null); } @@ -198,42 +162,6 @@ public abstract class AbstractServiceTest { return mapper.readTree(this.getClass().getClassLoader().getResourceAsStream(resourceName)); } - protected RuleMetaData generateRule(TenantId tenantId, Integer weight, String pluginToken) throws IOException { - if (tenantId == null) { - tenantId = new TenantId(UUIDs.timeBased()); - } - if (weight == null) { - weight = ThreadLocalRandom.current().nextInt(); - } - - RuleMetaData ruleMetaData = new RuleMetaData(); - ruleMetaData.setName("Testing"); - ruleMetaData.setTenantId(tenantId); - ruleMetaData.setWeight(weight); - ruleMetaData.setPluginToken(pluginToken); - - ruleMetaData.setAction(createNode(ComponentScope.TENANT, ComponentType.ACTION, - "org.thingsboard.component.ActionTest", "TestJsonDescriptor.json", "TestJsonData.json")); - ruleMetaData.setProcessor(createNode(ComponentScope.TENANT, ComponentType.PROCESSOR, - "org.thingsboard.component.ProcessorTest", "TestJsonDescriptor.json", "TestJsonData.json")); - ruleMetaData.setFilters(mapper.createArrayNode().add( - createNode(ComponentScope.TENANT, ComponentType.FILTER, - "org.thingsboard.component.FilterTest", "TestJsonDescriptor.json", "TestJsonData.json") - )); - - ruleMetaData.setAdditionalInfo(mapper.readTree("{}")); - return ruleMetaData; - } - - protected JsonNode createNode(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptor, String configuration) throws IOException { - getOrCreateDescriptor(scope, type, clazz, configurationDescriptor); - ObjectNode oNode = mapper.createObjectNode(); - oNode.set("name", new TextNode("test action")); - oNode.set("clazz", new TextNode(clazz)); - oNode.set("configuration", readFromResource(configuration)); - return oNode; - } - @Bean public AuditLogLevelFilter auditLogLevelFilter() { Map mask = new HashMap<>(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAdminSettingsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAdminSettingsServiceTest.java index 4ea39f0c85..41ba94f049 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAdminSettingsServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAdminSettingsServiceTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java index fd071e9c51..03099e9b75 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java @@ -21,7 +21,11 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.alarm.*; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmQuery; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; @@ -168,7 +172,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); - alarmService.clearAlarm(created.getId(), System.currentTimeMillis()).get(); + alarmService.clearAlarm(created.getId(), null, System.currentTimeMillis()).get(); created = alarmService.findAlarmByIdAsync(created.getId()).get(); alarms = alarmService.findAlarms(AlarmQuery.builder() diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index 993e5254cb..02b5186f8d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -32,10 +32,8 @@ import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; import java.io.IOException; -import java.sql.Time; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java index 61fbf05ebe..9269fc34d9 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsCacheTest.java @@ -36,7 +36,10 @@ import org.thingsboard.server.dao.device.DeviceService; import java.util.UUID; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public abstract class BaseDeviceCredentialsCacheTest extends AbstractServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsServiceTest.java index 032534e889..1d771b2840 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceCredentialsServiceTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.service; import com.datastax.driver.core.utils.UUIDs; -import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java index 3a0b6aef8f..bc78fd7ec3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationCacheTest.java @@ -34,7 +34,10 @@ import org.thingsboard.server.dao.relation.RelationService; import java.util.UUID; import java.util.concurrent.ExecutionException; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; public abstract class BaseRelationCacheTest extends AbstractServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java index 754454aebf..743aefcc40 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java @@ -24,12 +24,12 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntityTypeFilter; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.Collections; import java.util.List; @@ -96,7 +96,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { saveRelation(relationA); saveRelation(relationB); - Assert.assertTrue(relationService.deleteEntityRelationsAsync(childId).get()); + Assert.assertNull(relationService.deleteEntityRelationsAsync(childId).get()); Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java new file mode 100644 index 0000000000..28c61475ee --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -0,0 +1,362 @@ +/** + * Copyright © 2016-2018 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.service; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.exception.DataValidationException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by igor on 3/13/18. + */ +public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { + + private IdComparator idComparator = new IdComparator<>(); + private IdComparator ruleNodeIdComparator = new IdComparator<>(); + + private TenantId tenantId; + + @Before + public void before() { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + + @Test + public void testSaveRuleChain() throws IOException { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName("My RuleChain"); + + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + Assert.assertNotNull(savedRuleChain); + Assert.assertNotNull(savedRuleChain.getId()); + Assert.assertTrue(savedRuleChain.getCreatedTime() > 0); + Assert.assertEquals(ruleChain.getTenantId(), savedRuleChain.getTenantId()); + Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName()); + + savedRuleChain.setName("My new RuleChain"); + + ruleChainService.saveRuleChain(savedRuleChain); + RuleChain foundRuleChain = ruleChainService.findRuleChainById(savedRuleChain.getId()); + Assert.assertEquals(foundRuleChain.getName(), savedRuleChain.getName()); + + ruleChainService.deleteRuleChainById(savedRuleChain.getId()); + } + + @Test(expected = DataValidationException.class) + public void testSaveRuleChainWithEmptyName() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChainService.saveRuleChain(ruleChain); + } + + @Test(expected = DataValidationException.class) + public void testSaveRuleChainWithInvalidTenant() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("My RuleChain"); + ruleChain.setTenantId(new TenantId(UUIDs.timeBased())); + ruleChainService.saveRuleChain(ruleChain); + } + + @Test + public void testFindRuleChainById() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName("My RuleChain"); + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + RuleChain foundRuleChain = ruleChainService.findRuleChainById(savedRuleChain.getId()); + Assert.assertNotNull(foundRuleChain); + Assert.assertEquals(savedRuleChain, foundRuleChain); + ruleChainService.deleteRuleChainById(savedRuleChain.getId()); + } + + @Test + public void testDeleteRuleChain() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName("My RuleChain"); + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + RuleChain foundRuleChain = ruleChainService.findRuleChainById(savedRuleChain.getId()); + Assert.assertNotNull(foundRuleChain); + ruleChainService.deleteRuleChainById(savedRuleChain.getId()); + foundRuleChain = ruleChainService.findRuleChainById(savedRuleChain.getId()); + Assert.assertNull(foundRuleChain); + } + + @Test + public void testFindRuleChainsByTenantId() { + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant = tenantService.saveTenant(tenant); + + TenantId tenantId = tenant.getId(); + + List ruleChains = new ArrayList<>(); + for (int i = 0; i < 165; i++) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName("RuleChain" + i); + ruleChains.add(ruleChainService.saveRuleChain(ruleChain)); + } + + List loadedRuleChains = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(16); + TextPageData pageData = null; + do { + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + loadedRuleChains.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(ruleChains, idComparator); + Collections.sort(loadedRuleChains, idComparator); + + Assert.assertEquals(ruleChains, loadedRuleChains); + + ruleChainService.deleteRuleChainsByTenantId(tenantId); + + pageLink = new TextPageLink(31); + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertTrue(pageData.getData().isEmpty()); + + tenantService.deleteTenant(tenantId); + } + + @Test + public void testFindRuleChainsByTenantIdAndName() { + String name1 = "RuleChain name 1"; + List ruleChainsName1 = new ArrayList<>(); + for (int i = 0; i < 123; i++) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 17)); + String name = name1 + suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + ruleChain.setName(name); + ruleChainsName1.add(ruleChainService.saveRuleChain(ruleChain)); + } + String name2 = "RuleChain name 2"; + List ruleChainsName2 = new ArrayList<>(); + for (int i = 0; i < 193; i++) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15)); + String name = name2 + suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + ruleChain.setName(name); + ruleChainsName2.add(ruleChainService.saveRuleChain(ruleChain)); + } + + List loadedRuleChainsName1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(19, name1); + TextPageData pageData = null; + do { + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + loadedRuleChainsName1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(ruleChainsName1, idComparator); + Collections.sort(loadedRuleChainsName1, idComparator); + + Assert.assertEquals(ruleChainsName1, loadedRuleChainsName1); + + List loadedRuleChainsName2 = new ArrayList<>(); + pageLink = new TextPageLink(4, name2); + do { + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + loadedRuleChainsName2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(ruleChainsName2, idComparator); + Collections.sort(loadedRuleChainsName2, idComparator); + + Assert.assertEquals(ruleChainsName2, loadedRuleChainsName2); + + for (RuleChain ruleChain : loadedRuleChainsName1) { + ruleChainService.deleteRuleChainById(ruleChain.getId()); + } + + pageLink = new TextPageLink(4, name1); + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (RuleChain ruleChain : loadedRuleChainsName2) { + ruleChainService.deleteRuleChainById(ruleChain.getId()); + } + + pageLink = new TextPageLink(4, name2); + pageData = ruleChainService.findTenantRuleChains(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testSaveRuleChainMetaData() throws Exception { + + RuleChainMetaData savedRuleChainMetaData = createRuleChainMetadata(); + + Assert.assertEquals(3, savedRuleChainMetaData.getNodes().size()); + Assert.assertEquals(3, savedRuleChainMetaData.getConnections().size()); + + for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) { + Assert.assertNotNull(ruleNode.getId()); + List relations = ruleChainService.getRuleNodeRelations(ruleNode.getId()); + if ("name1".equals(ruleNode.getName())) { + Assert.assertEquals(2, relations.size()); + } else if ("name2".equals(ruleNode.getName())) { + Assert.assertEquals(1, relations.size()); + } else if ("name3".equals(ruleNode.getName())) { + Assert.assertEquals(0, relations.size()); + } + } + + List loadedRuleNodes = ruleChainService.getRuleChainNodes(savedRuleChainMetaData.getRuleChainId()); + + Collections.sort(savedRuleChainMetaData.getNodes(), ruleNodeIdComparator); + Collections.sort(loadedRuleNodes, ruleNodeIdComparator); + + Assert.assertEquals(savedRuleChainMetaData.getNodes(), loadedRuleNodes); + + ruleChainService.deleteRuleChainById(savedRuleChainMetaData.getRuleChainId()); + } + + @Test + public void testUpdateRuleChainMetaData() throws Exception { + RuleChainMetaData savedRuleChainMetaData = createRuleChainMetadata(); + + List ruleNodes = savedRuleChainMetaData.getNodes(); + int name3Index = -1; + for (int i=0;i relations = ruleChainService.getRuleNodeRelations(ruleNode.getId()); + if ("name1".equals(ruleNode.getName())) { + Assert.assertEquals(2, relations.size()); + } else if ("name2".equals(ruleNode.getName())) { + Assert.assertEquals(1, relations.size()); + } else if ("name4".equals(ruleNode.getName())) { + Assert.assertEquals(0, relations.size()); + } + } + + List loadedRuleNodes = ruleChainService.getRuleChainNodes(savedRuleChainMetaData.getRuleChainId()); + + Collections.sort(updatedRuleChainMetaData.getNodes(), ruleNodeIdComparator); + Collections.sort(loadedRuleNodes, ruleNodeIdComparator); + + Assert.assertEquals(updatedRuleChainMetaData.getNodes(), loadedRuleNodes); + + ruleChainService.deleteRuleChainById(savedRuleChainMetaData.getRuleChainId()); + } + + private RuleChainMetaData createRuleChainMetadata() throws Exception { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("My RuleChain"); + ruleChain.setTenantId(tenantId); + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); + ruleChainMetaData.setRuleChainId(savedRuleChain.getId()); + + ObjectMapper mapper = new ObjectMapper(); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("name1"); + ruleNode1.setType("type1"); + ruleNode1.setConfiguration(mapper.readTree("\"key1\": \"val1\"")); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("name2"); + ruleNode2.setType("type2"); + ruleNode2.setConfiguration(mapper.readTree("\"key2\": \"val2\"")); + + RuleNode ruleNode3 = new RuleNode(); + ruleNode3.setName("name3"); + ruleNode3.setType("type3"); + ruleNode3.setConfiguration(mapper.readTree("\"key3\": \"val3\"")); + + List ruleNodes = new ArrayList<>(); + ruleNodes.add(ruleNode1); + ruleNodes.add(ruleNode2); + ruleNodes.add(ruleNode3); + ruleChainMetaData.setFirstNodeIndex(0); + ruleChainMetaData.setNodes(ruleNodes); + + ruleChainMetaData.addConnectionInfo(0,1,"success"); + ruleChainMetaData.addConnectionInfo(0,2,"fail"); + ruleChainMetaData.addConnectionInfo(1,2,"success"); + + return ruleChainService.saveRuleChainMetaData(ruleChainMetaData); + } + + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DaoNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DaoNoSqlTest.java index 9263bc4797..82a1a57f1f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DaoNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DaoNoSqlTest.java @@ -17,7 +17,12 @@ package org.thingsboard.server.dao.service; import org.springframework.test.context.TestPropertySource; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DaoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DaoSqlTest.java index 285c653d63..ea95650416 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DaoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DaoSqlTest.java @@ -17,7 +17,12 @@ package org.thingsboard.server.dao.service; import org.springframework.test.context.TestPropertySource; -import java.lang.annotation.*; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java index 62af5a548a..eb25c86c0c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/event/BaseEventServiceTest.java @@ -20,10 +20,10 @@ import org.junit.Assert; import org.junit.Test; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EventId; -import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageLink; @@ -66,15 +66,15 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { long endTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 0).toEpochSecond(ZoneOffset.UTC); long timeAfterEndTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); - RuleId ruleId = new RuleId(UUIDs.timeBased()); + CustomerId customerId = new CustomerId(UUIDs.timeBased()); TenantId tenantId = new TenantId(UUIDs.timeBased()); - saveEventWithProvidedTime(timeBeforeStartTime, ruleId, tenantId); - Event savedEvent = saveEventWithProvidedTime(eventTime, ruleId, tenantId); - Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, ruleId, tenantId); - Event savedEvent3 = saveEventWithProvidedTime(eventTime+2, ruleId, tenantId); - saveEventWithProvidedTime(timeAfterEndTime, ruleId, tenantId); + saveEventWithProvidedTime(timeBeforeStartTime, customerId, tenantId); + Event savedEvent = saveEventWithProvidedTime(eventTime, customerId, tenantId); + Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, customerId, tenantId); + Event savedEvent3 = saveEventWithProvidedTime(eventTime+2, customerId, tenantId); + saveEventWithProvidedTime(timeAfterEndTime, customerId, tenantId); - TimePageData events = eventService.findEvents(tenantId, ruleId, DataConstants.STATS, + TimePageData events = eventService.findEvents(tenantId, customerId, DataConstants.STATS, new TimePageLink(2, startTime, endTime, true)); Assert.assertNotNull(events.getData()); @@ -84,7 +84,7 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { Assert.assertTrue(events.hasNext()); Assert.assertNotNull(events.getNextPageLink()); - events = eventService.findEvents(tenantId, ruleId, DataConstants.STATS, events.getNextPageLink()); + events = eventService.findEvents(tenantId, customerId, DataConstants.STATS, events.getNextPageLink()); Assert.assertNotNull(events.getData()); Assert.assertTrue(events.getData().size() == 1); @@ -101,15 +101,15 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { long endTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 0).toEpochSecond(ZoneOffset.UTC); long timeAfterEndTime = LocalDateTime.of(2016, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); - RuleId ruleId = new RuleId(UUIDs.timeBased()); + CustomerId customerId = new CustomerId(UUIDs.timeBased()); TenantId tenantId = new TenantId(UUIDs.timeBased()); - saveEventWithProvidedTime(timeBeforeStartTime, ruleId, tenantId); - Event savedEvent = saveEventWithProvidedTime(eventTime, ruleId, tenantId); - Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, ruleId, tenantId); - Event savedEvent3 = saveEventWithProvidedTime(eventTime+2, ruleId, tenantId); - saveEventWithProvidedTime(timeAfterEndTime, ruleId, tenantId); + saveEventWithProvidedTime(timeBeforeStartTime, customerId, tenantId); + Event savedEvent = saveEventWithProvidedTime(eventTime, customerId, tenantId); + Event savedEvent2 = saveEventWithProvidedTime(eventTime+1, customerId, tenantId); + Event savedEvent3 = saveEventWithProvidedTime(eventTime+2, customerId, tenantId); + saveEventWithProvidedTime(timeAfterEndTime, customerId, tenantId); - TimePageData events = eventService.findEvents(tenantId, ruleId, DataConstants.STATS, + TimePageData events = eventService.findEvents(tenantId, customerId, DataConstants.STATS, new TimePageLink(2, startTime, endTime, false)); Assert.assertNotNull(events.getData()); @@ -119,7 +119,7 @@ public abstract class BaseEventServiceTest extends AbstractServiceTest { Assert.assertTrue(events.hasNext()); Assert.assertNotNull(events.getNextPageLink()); - events = eventService.findEvents(tenantId, ruleId, DataConstants.STATS, events.getNextPageLink()); + events = eventService.findEvents(tenantId, customerId, DataConstants.STATS, events.getNextPageLink()); Assert.assertNotNull(events.getData()); Assert.assertTrue(events.getData().size() == 1); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AdminSettingsServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AdminSettingsServiceNoSqlTest.java index c30f864bad..97e9e81ab8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AdminSettingsServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AdminSettingsServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseAdminSettingsServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class AdminSettingsServiceNoSqlTest extends BaseAdminSettingsServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AlarmServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AlarmServiceNoSqlTest.java index f822adbcfa..499d3c6427 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AlarmServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AlarmServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseAlarmServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class AlarmServiceNoSqlTest extends BaseAlarmServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AssetServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AssetServiceNoSqlTest.java index 3887788b0b..9543560140 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AssetServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/AssetServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseAssetServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class AssetServiceNoSqlTest extends BaseAssetServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/CustomerServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/CustomerServiceNoSqlTest.java index 99c77de1f7..905b723d8c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/CustomerServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/CustomerServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseCustomerServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class CustomerServiceNoSqlTest extends BaseCustomerServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DashboardServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DashboardServiceNoSqlTest.java index 3234805a8d..9712ee896e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DashboardServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DashboardServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseDashboardServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class DashboardServiceNoSqlTest extends BaseDashboardServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialCacheNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialCacheNoSqlTest.java index 218993f42b..7c1356d28a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialCacheNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialCacheNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceCredentialsCacheTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class DeviceCredentialCacheNoSqlTest extends BaseDeviceCredentialsCacheTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialServiceNoSqlTest.java index a12658e185..6b8cf64f03 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceCredentialServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceCredentialsServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class DeviceCredentialServiceNoSqlTest extends BaseDeviceCredentialsServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceServiceNoSqlTest.java index 5d012782b3..99e327ca0e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/DeviceServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class DeviceServiceNoSqlTest extends BaseDeviceServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RelationServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RelationServiceNoSqlTest.java index 48d20262a9..74728fbb6d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RelationServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RelationServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseRelationServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class RelationServiceNoSqlTest extends BaseRelationServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RuleChainServiceNoSqlTest.java similarity index 78% rename from dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/nosql/RuleChainServiceNoSqlTest.java index 7ff9066499..0427eebe05 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/nosql/RuleServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/RuleChainServiceNoSqlTest.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.rule.nosql; +package org.thingsboard.server.dao.service.nosql; +import org.thingsboard.server.dao.service.BaseRuleChainServiceTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest; @DaoNoSqlTest -public class RuleServiceNoSqlTest extends BaseRuleServiceTest { +public class RuleChainServiceNoSqlTest extends BaseRuleChainServiceTest { } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/TenantServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/TenantServiceNoSqlTest.java index e19cd51e84..de474fd341 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/TenantServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/TenantServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseTenantServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class TenantServiceNoSqlTest extends BaseTenantServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/UserServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/UserServiceNoSqlTest.java index 0aa7175e24..af3c497436 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/UserServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/UserServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseUserServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class UserServiceNoSqlTest extends BaseUserServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetTypeServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetTypeServiceNoSqlTest.java index 98d867cd3c..90399c487e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetTypeServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetTypeServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseWidgetTypeServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class WidgetTypeServiceNoSqlTest extends BaseWidgetTypeServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetsBundleServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetsBundleServiceNoSqlTest.java index 00b131decc..79b5507156 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetsBundleServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/nosql/WidgetsBundleServiceNoSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.nosql; -import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.BaseWidgetsBundleServiceTest; +import org.thingsboard.server.dao.service.DaoNoSqlTest; @DaoNoSqlTest public class WidgetsBundleServiceNoSqlTest extends BaseWidgetsBundleServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/BasePluginServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/plugin/BasePluginServiceTest.java deleted file mode 100644 index d9f551fb79..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/BasePluginServiceTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright © 2016-2018 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.service.plugin; - -import com.datastax.driver.core.utils.UUIDs; -import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; -import org.junit.Test; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.service.AbstractServiceTest; - -import java.util.UUID; - -@Slf4j -public abstract class BasePluginServiceTest extends AbstractServiceTest { - - @Test - public void savePlugin() throws Exception { - PluginMetaData pluginMetaData = pluginService.savePlugin(generatePlugin(null, null)); - Assert.assertNotNull(pluginMetaData.getId()); - Assert.assertNotNull(pluginMetaData.getAdditionalInfo()); - - pluginMetaData.setAdditionalInfo(mapper.readTree("{\"description\":\"test\"}")); - PluginMetaData newPluginMetaData = pluginService.savePlugin(pluginMetaData); - Assert.assertEquals(pluginMetaData.getAdditionalInfo(), newPluginMetaData.getAdditionalInfo()); - - } - - @Test - public void findPluginById() throws Exception { - PluginMetaData expected = pluginService.savePlugin(generatePlugin(null, null)); - Assert.assertNotNull(expected.getId()); - PluginMetaData found = pluginService.findPluginById(expected.getId()); - Assert.assertEquals(expected, found); - } - - @Test - public void findPluginByTenantIdAndApiToken() throws Exception { - String token = UUID.randomUUID().toString(); - TenantId tenantId = new TenantId(UUIDs.timeBased()); - pluginService.savePlugin(generatePlugin(null, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - PluginMetaData expected = pluginService.savePlugin(generatePlugin(tenantId, token)); - Assert.assertNotNull(expected.getId()); - PluginMetaData found = pluginService.findPluginByApiToken(token); - Assert.assertEquals(expected, found); - } - - @Test - public void findSystemPlugins() throws Exception { - TenantId systemTenant = new TenantId(ModelConstants.NULL_UUID); // system tenant id - pluginService.savePlugin(generatePlugin(null, null)); - pluginService.savePlugin(generatePlugin(null, null)); - pluginService.savePlugin(generatePlugin(systemTenant, null)); - pluginService.savePlugin(generatePlugin(systemTenant, null)); - TextPageData found = pluginService.findSystemPlugins(new TextPageLink(100)); - Assert.assertEquals(2, found.getData().size()); - Assert.assertFalse(found.hasNext()); - } - - @Test - public void findTenantPlugins() throws Exception { - TenantId tenantId = new TenantId(UUIDs.timeBased()); - pluginService.savePlugin(generatePlugin(null, null)); - pluginService.savePlugin(generatePlugin(null, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - TextPageData found = pluginService.findTenantPlugins(tenantId, new TextPageLink(100)); - Assert.assertEquals(3, found.getData().size()); - } - - @Test - public void deletePluginById() throws Exception { - PluginMetaData expected = pluginService.savePlugin(generatePlugin(null, null)); - Assert.assertNotNull(expected.getId()); - pluginService.deletePluginById(expected.getId()); - PluginMetaData found = pluginService.findPluginById(expected.getId()); - Assert.assertNull(found); - } - - @Test - public void deletePluginsByTenantId() throws Exception { - TenantId tenantId = new TenantId(UUIDs.timeBased()); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - pluginService.savePlugin(generatePlugin(tenantId, null)); - TextPageData found = pluginService.findTenantPlugins(tenantId, new TextPageLink(100)); - Assert.assertEquals(3, found.getData().size()); - pluginService.deletePluginsByTenantId(tenantId); - found = pluginService.findTenantPlugins(tenantId, new TextPageLink(100)); - Assert.assertEquals(0, found.getData().size()); - } - -} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/nosql/PluginServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/plugin/nosql/PluginServiceNoSqlTest.java deleted file mode 100644 index 84539fe23f..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/plugin/nosql/PluginServiceNoSqlTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2018 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.service.plugin.nosql; - -import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.dao.service.plugin.BasePluginServiceTest; - -@DaoNoSqlTest -public class PluginServiceNoSqlTest extends BasePluginServiceTest { -} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java deleted file mode 100644 index b6139df898..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/BaseRuleServiceTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright © 2016-2018 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.service.rule; - -import com.datastax.driver.core.utils.UUIDs; -import org.junit.Assert; -import org.junit.Test; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageData; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.service.AbstractServiceTest; - -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; - -public abstract class BaseRuleServiceTest extends AbstractServiceTest { - - @Test - public void saveRule() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - RuleMetaData ruleMetaData = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(ruleMetaData.getId()); - Assert.assertNotNull(ruleMetaData.getAdditionalInfo()); - ruleMetaData.setAdditionalInfo(mapper.readTree("{\"description\":\"test\"}")); - RuleMetaData newRuleMetaData = ruleService.saveRule(ruleMetaData); - Assert.assertEquals(ruleMetaData.getAdditionalInfo(), newRuleMetaData.getAdditionalInfo()); - } - - @Test - public void findRuleById() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(expected.getId()); - RuleMetaData found = ruleService.findRuleById(expected.getId()); - Assert.assertEquals(expected, found); - } - - @Test - public void findPluginRules() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - List foundA = ruleService.findPluginRules(pluginA.getApiToken()); - Assert.assertEquals(3, foundA.size()); - - List foundB = ruleService.findPluginRules(pluginB.getApiToken()); - Assert.assertEquals(2, foundB.size()); - } - - @Test - public void findSystemRules() throws Exception { - TenantId systemTenant = new TenantId(ModelConstants.NULL_UUID); // system tenant id - - PluginMetaData plugin = generatePlugin(systemTenant, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - ruleService.saveRule(generateRule(systemTenant, null, plugin.getApiToken())); - TextPageData found = ruleService.findSystemRules(new TextPageLink(100)); - Assert.assertEquals(3, found.getData().size()); - } - - @Test - public void findTenantRules() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - TextPageData foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(3, foundA.getData().size()); - - TextPageData foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - } - - @Test - public void deleteRuleById() throws Exception { - PluginMetaData plugin = generatePlugin(null, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(plugin); - - RuleMetaData expected = ruleService.saveRule(generateRule(plugin.getTenantId(), null, plugin.getApiToken())); - Assert.assertNotNull(expected.getId()); - RuleMetaData found = ruleService.findRuleById(expected.getId()); - Assert.assertEquals(expected, found); - ruleService.deleteRuleById(expected.getId()); - found = ruleService.findRuleById(expected.getId()); - Assert.assertNull(found); - } - - @Test - public void deleteRulesByTenantId() throws Exception { - TenantId tenantIdA = new TenantId(UUIDs.timeBased()); - TenantId tenantIdB = new TenantId(UUIDs.timeBased()); - - PluginMetaData pluginA = generatePlugin(tenantIdA, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - PluginMetaData pluginB = generatePlugin(tenantIdB, "testPluginToken" + ThreadLocalRandom.current().nextInt()); - pluginService.savePlugin(pluginA); - pluginService.savePlugin(pluginB); - - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - ruleService.saveRule(generateRule(tenantIdA, null, pluginA.getApiToken())); - - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - ruleService.saveRule(generateRule(tenantIdB, null, pluginB.getApiToken())); - - TextPageData foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(3, foundA.getData().size()); - - TextPageData foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - - ruleService.deleteRulesByTenantId(tenantIdA); - - foundA = ruleService.findTenantRules(tenantIdA, new TextPageLink(100)); - Assert.assertEquals(0, foundA.getData().size()); - - foundB = ruleService.findTenantRules(tenantIdB, new TextPageLink(100)); - Assert.assertEquals(2, foundB.getData().size()); - } -} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AdminSettingsServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AdminSettingsServiceSqlTest.java index d5a69c06d0..aa2015fb59 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AdminSettingsServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AdminSettingsServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseAdminSettingsServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class AdminSettingsServiceSqlTest extends BaseAdminSettingsServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AlarmServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AlarmServiceSqlTest.java index 38d60800c9..7b2d3a7e92 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AlarmServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AlarmServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseAlarmServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class AlarmServiceSqlTest extends BaseAlarmServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AssetServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AssetServiceSqlTest.java index 3020cec42a..fc77fe38a8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/AssetServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/AssetServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseAssetServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class AssetServiceSqlTest extends BaseAssetServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/CustomerServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/CustomerServiceSqlTest.java index c900cc4909..48b4bac961 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/CustomerServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/CustomerServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseCustomerServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class CustomerServiceSqlTest extends BaseCustomerServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DashboardServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DashboardServiceSqlTest.java index 66aee43ecf..4c6a2bbf39 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DashboardServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DashboardServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseDashboardServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class DashboardServiceSqlTest extends BaseDashboardServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsCacheSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsCacheSqlTest.java index 27febf8219..2f534f6a79 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsCacheSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsCacheSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceCredentialsCacheTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class DeviceCredentialsCacheSqlTest extends BaseDeviceCredentialsCacheTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsServiceSqlTest.java index f44c004f51..78e84ebf63 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceCredentialsServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceCredentialsServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class DeviceCredentialsServiceSqlTest extends BaseDeviceCredentialsServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceServiceSqlTest.java index f076237325..5576a3795a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/DeviceServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseDeviceServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class DeviceServiceSqlTest extends BaseDeviceServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/RelationServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/RelationServiceSqlTest.java index b2410db223..a88ecd2d0a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/RelationServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/RelationServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseRelationServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class RelationServiceSqlTest extends BaseRelationServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/RuleChainServiceSqlTest.java similarity index 78% rename from dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/sql/RuleChainServiceSqlTest.java index da3d4a0b7b..d877037ddb 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/rule/sql/RuleServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/RuleChainServiceSqlTest.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.rule.sql; +package org.thingsboard.server.dao.service.sql; +import org.thingsboard.server.dao.service.BaseRuleChainServiceTest; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.service.rule.BaseRuleServiceTest; @DaoSqlTest -public class RuleServiceSqlTest extends BaseRuleServiceTest { +public class RuleChainServiceSqlTest extends BaseRuleChainServiceTest { } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/TenantServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/TenantServiceSqlTest.java index 30c2d8ad1d..abd847fc39 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/TenantServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/TenantServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseTenantServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class TenantServiceSqlTest extends BaseTenantServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/UserServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/UserServiceSqlTest.java index 2e9007cea5..b81c0be984 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/UserServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/UserServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseUserServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class UserServiceSqlTest extends BaseUserServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetTypeServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetTypeServiceSqlTest.java index de2b0e6007..03b188c6dc 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetTypeServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetTypeServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseWidgetTypeServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class WidgetTypeServiceSqlTest extends BaseWidgetTypeServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetsBundleServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetsBundleServiceSqlTest.java index 79a7e7b8b4..0b78badd22 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetsBundleServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/WidgetsBundleServiceSqlTest.java @@ -15,8 +15,8 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.BaseWidgetsBundleServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest public class WidgetsBundleServiceSqlTest extends BaseWidgetsBundleServiceTest { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java index 0cb3f7fbdd..f045bf112f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java @@ -20,7 +20,15 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.service.AbstractServiceTest; import java.util.ArrayList; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java index 19674f8982..8139c3b104 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java @@ -35,7 +35,9 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import static junit.framework.TestCase.assertFalse; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * Created by Valerii Sosliuk on 5/21/2017. diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDaoTest.java index 9874efff77..cdc5515164 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDaoTest.java @@ -41,23 +41,23 @@ public class JpaBaseComponentDescriptorDaoTest extends AbstractJpaDaoTest { @Test public void findByType() { for (int i = 0; i < 20; i++) { - createComponentDescriptor(ComponentType.PLUGIN, ComponentScope.SYSTEM, i); + createComponentDescriptor(ComponentType.FILTER, ComponentScope.SYSTEM, i); createComponentDescriptor(ComponentType.ACTION, ComponentScope.TENANT, i + 20); } TextPageLink pageLink1 = new TextPageLink(15, "COMPONENT_"); - List components1 = componentDescriptorDao.findByTypeAndPageLink(ComponentType.PLUGIN, pageLink1); + List components1 = componentDescriptorDao.findByTypeAndPageLink(ComponentType.FILTER, pageLink1); assertEquals(15, components1.size()); TextPageLink pageLink2 = new TextPageLink(15, "COMPONENT_", components1.get(14).getId().getId(), null); - List components2 = componentDescriptorDao.findByTypeAndPageLink(ComponentType.PLUGIN, pageLink2); + List components2 = componentDescriptorDao.findByTypeAndPageLink(ComponentType.FILTER, pageLink2); assertEquals(5, components2.size()); } @Test public void findByTypeAndSocpe() { for (int i = 0; i < 20; i++) { - createComponentDescriptor(ComponentType.PLUGIN, ComponentScope.SYSTEM, i); + createComponentDescriptor(ComponentType.ENRICHMENT, ComponentScope.SYSTEM, i); createComponentDescriptor(ComponentType.ACTION, ComponentScope.TENANT, i + 20); createComponentDescriptor(ComponentType.FILTER, ComponentScope.SYSTEM, i + 40); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java index aa80339ef8..0d48d43c36 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java @@ -20,7 +20,6 @@ import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.DashboardInfo; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java index 67841f5a53..84ae307c05 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDaoTest.java @@ -36,7 +36,10 @@ import java.util.List; import java.util.Optional; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.thingsboard.server.common.data.DataConstants.ALARM; import static org.thingsboard.server.common.data.DataConstants.STATS; diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/plugin/JpaBasePluginDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/plugin/JpaBasePluginDaoTest.java deleted file mode 100644 index 792031dcfd..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/plugin/JpaBasePluginDaoTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright © 2016-2018 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.plugin; - -import com.datastax.driver.core.utils.UUIDs; -import com.github.springtestdbunit.annotation.DatabaseSetup; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.plugin.PluginMetaData; -import org.thingsboard.server.dao.AbstractJpaDaoTest; -import org.thingsboard.server.dao.plugin.PluginDao; - -import java.util.List; -import java.util.UUID; - -import static org.junit.Assert.assertEquals; - -/** - * Created by Valerii Sosliuk on 5/1/2017. - */ -public class JpaBasePluginDaoTest extends AbstractJpaDaoTest { - - @Autowired - private PluginDao pluginDao; - - @Test - @DatabaseSetup("classpath:dbunit/empty_dataset.xml") - public void testFindByTenantIdAndPageLink() { - UUID tenantId1 = UUIDs.timeBased(); - UUID tenantId2 = UUIDs.timeBased(); - createPluginsTwoTenants(tenantId1, tenantId2, "plugin_"); - List rules1 = pluginDao.findByTenantIdAndPageLink( - new TenantId(tenantId1), new TextPageLink(20, "plugin_")); - assertEquals(20, rules1.size()); - - List rules2 = pluginDao.findByTenantIdAndPageLink(new TenantId(tenantId1), - new TextPageLink(20, "plugin_", rules1.get(19).getId().getId(), null)); - assertEquals(10, rules2.size()); - - List rules3 = pluginDao.findByTenantIdAndPageLink(new TenantId(tenantId1), - new TextPageLink(20, "plugin_", rules2.get(9).getId().getId(), null)); - assertEquals(0, rules3.size()); - } - - @Test - @DatabaseSetup(value = "classpath:dbunit/empty_dataset.xml") - public void testFindAllTenantRulesByTenantId() { - UUID tenantId1 = UUIDs.timeBased(); - UUID tenantId2 = UUIDs.timeBased(); - createTenantsAndSystemPlugins(tenantId1, tenantId2, "name_"); - List rules1 = pluginDao.findAllTenantPluginsByTenantId( - tenantId1, new TextPageLink(40, "name_")); - assertEquals(40, rules1.size()); - - List rules2 = pluginDao.findAllTenantPluginsByTenantId(tenantId1, - new TextPageLink(40, "name_", rules1.get(19).getId().getId(), null)); - assertEquals(20, rules2.size()); - - List rules3 = pluginDao.findAllTenantPluginsByTenantId(tenantId1, - new TextPageLink(40, "name_", rules2.get(19).getId().getId(), null)); - assertEquals(0, rules3.size()); - } - - private void createTenantsAndSystemPlugins(UUID tenantId1, UUID tenantId2, String namePrefix) { - for (int i = 0; i < 40; i++) { - createPlugin(tenantId1, namePrefix, i); - createPlugin(tenantId2, namePrefix, i); - createPlugin(null, namePrefix, i); - } - } - - private void createPluginsTwoTenants(UUID tenantId1, UUID tenantId2, String namePrefix) { - for (int i = 0; i < 30; i++) { - createPlugin(tenantId1, namePrefix, i); - createPlugin(tenantId2, namePrefix, i); - } - } - - private void createPlugin(UUID tenantId, String namePrefix, int i) { - PluginMetaData plugin = new PluginMetaData(); - plugin.setId(new PluginId(UUIDs.timeBased())); - plugin.setTenantId(new TenantId(tenantId)); - plugin.setName(namePrefix + i); - pluginDao.save(plugin); - } -} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDaoTest.java deleted file mode 100644 index 16b6965dd6..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaBaseRuleDaoTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright © 2016-2018 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.rule; - -import com.datastax.driver.core.utils.UUIDs; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.springtestdbunit.annotation.DatabaseSetup; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.TextPageLink; -import org.thingsboard.server.common.data.rule.RuleMetaData; -import org.thingsboard.server.dao.AbstractJpaDaoTest; -import org.thingsboard.server.dao.rule.RuleDao; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; - -import static org.junit.Assert.*; - -/** - * Created by Valerii Sosliuk on 4/30/2017. - */ -public class JpaBaseRuleDaoTest extends AbstractJpaDaoTest { - - @Autowired - private RuleDao ruleDao; - - @Test - @DatabaseSetup("classpath:dbunit/empty_dataset.xml") - public void testSave() throws IOException { - UUID id = UUIDs.timeBased(); - RuleMetaData ruleMetaData = getRuleMetaData(id); - String filters = "{\"filters\":\"value-1\"}"; - String processor = "{\"processor\":\"value-2\"}"; - String action = "{\"action\":\"value-3\"}"; - String additionalInfo = "{\"additionalInfo\":\"value-4\"}"; - ObjectMapper mapper = new ObjectMapper(); - ruleMetaData.setFilters(mapper.readTree(filters)); - ruleMetaData.setProcessor(mapper.readTree(processor)); - ruleMetaData.setAction(mapper.readTree(action)); - ruleMetaData.setAdditionalInfo(mapper.readTree(additionalInfo)); - ruleDao.save(ruleMetaData); - RuleMetaData savedRule = ruleDao.findById(id); - assertNotNull(savedRule); - assertEquals(filters, savedRule.getFilters().toString()); - assertEquals(processor, savedRule.getProcessor().toString()); - assertEquals(action, savedRule.getAction().toString()); - assertEquals(additionalInfo, savedRule.getAdditionalInfo().toString()); - } - - @Test - @DatabaseSetup("classpath:dbunit/empty_dataset.xml") - public void testDelete() throws IOException { - UUID id = UUIDs.timeBased(); - RuleMetaData ruleMetaData = getRuleMetaData(id); - ruleDao.save(ruleMetaData); - RuleMetaData savedRule = ruleDao.findById(id); - assertNotNull(savedRule); - assertTrue(ruleDao.removeById(id)); - RuleMetaData afterDelete = ruleDao.findById(id); - assertNull(afterDelete); - } - - @Test - @DatabaseSetup("classpath:dbunit/rule.xml") - public void testFindRulesByPlugin() { - assertEquals(3, ruleDao.findRulesByPlugin("token_1").size()); - } - - @Test - @DatabaseSetup("classpath:dbunit/empty_dataset.xml") - public void testFindByTenantIdAndPageLink() { - UUID tenantId1 = UUIDs.timeBased(); - UUID tenantId2 = UUIDs.timeBased(); - createRulesTwoTenants(tenantId1, tenantId2, "name_", "token"); - List rules1 = ruleDao.findByTenantIdAndPageLink( - new TenantId(tenantId1), new TextPageLink(20, "name_")); - assertEquals(20, rules1.size()); - - List rules2 = ruleDao.findByTenantIdAndPageLink(new TenantId(tenantId1), - new TextPageLink(20, "name_", rules1.get(19).getId().getId(), null)); - assertEquals(10, rules2.size()); - - List rules3 = ruleDao.findByTenantIdAndPageLink(new TenantId(tenantId1), - new TextPageLink(20, "name_", rules2.get(9).getId().getId(), null)); - assertEquals(0, rules3.size()); - } - - @Test - @DatabaseSetup("classpath:dbunit/empty_dataset.xml") - public void testFindAllTenantRulesByTenantId() { - UUID tenantId1 = UUIDs.timeBased(); - UUID tenantId2 = UUIDs.timeBased(); - createTenantsAndSystemRules(tenantId1, tenantId2, "name_", "token"); - List rules1 = ruleDao.findAllTenantRulesByTenantId( - tenantId1, new TextPageLink(40, "name_")); - assertEquals(40, rules1.size()); - - List rules2 = ruleDao.findAllTenantRulesByTenantId(tenantId1, - new TextPageLink(40, "name_", rules1.get(19).getId().getId(), null)); - assertEquals(20, rules2.size()); - - List rules3 = ruleDao.findAllTenantRulesByTenantId(tenantId1, - new TextPageLink(40, "name_", rules2.get(19).getId().getId(), null)); - assertEquals(0, rules3.size()); - } - - private void createRulesTwoTenants(UUID tenantId1, UUID tenantId2, String namePrefix, String pluginToken) { - for (int i = 0; i < 30; i++) { - createRule(tenantId1, namePrefix, pluginToken, i); - createRule(tenantId2, namePrefix, pluginToken, i); - } - } - - private void createTenantsAndSystemRules(UUID tenantId1, UUID tenantId2, String namePrefix, String pluginToken) { - for (int i = 0; i < 40; i++) { - createRule(tenantId1, namePrefix, pluginToken, i); - createRule(tenantId2, namePrefix, pluginToken, i); - createRule(null, namePrefix, pluginToken, i); - } - } - - private void createRule(UUID tenantId, String namePrefix, String pluginToken, int i) { - RuleMetaData ruleMetaData = new RuleMetaData(); - ruleMetaData.setId(new RuleId(UUIDs.timeBased())); - ruleMetaData.setTenantId(new TenantId(tenantId)); - ruleMetaData.setName(namePrefix + i); - ruleMetaData.setPluginToken(pluginToken); - ruleDao.save(ruleMetaData); - } - - private RuleMetaData getRuleMetaData(UUID id) throws IOException { - RuleMetaData ruleMetaData = new RuleMetaData(); - ruleMetaData.setId(new RuleId(id)); - ruleMetaData.setTenantId(new TenantId(UUIDs.timeBased())); - ruleMetaData.setName("test"); - - return ruleMetaData; - } -} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java index 43b87b4cd9..bf36711100 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDaoTest.java @@ -27,7 +27,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** * Created by Valerii Sosliuk on 4/30/2017. diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java index 67491a2361..bacd73d13e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/user/JpaUserDaoTest.java @@ -34,7 +34,8 @@ import java.io.IOException; import java.util.List; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; /** diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java index 47cf0341d0..12487a81a3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java @@ -31,7 +31,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleDao; import java.util.List; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; /** diff --git a/dao/src/test/java/org/thingsboard/server/dao/util/BufferedRateLimiterTest.java b/dao/src/test/java/org/thingsboard/server/dao/util/BufferedRateLimiterTest.java index 67c3ce8d73..366cefd5d8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/util/BufferedRateLimiterTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/util/BufferedRateLimiterTest.java @@ -15,7 +15,11 @@ */ package org.thingsboard.server.dao.util; -import com.google.common.util.concurrent.*; +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.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import org.junit.Test; import org.thingsboard.server.dao.exception.BufferLimitException; @@ -25,7 +29,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class BufferedRateLimiterTest { diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 42b71f84e3..f2dab45491 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -28,3 +28,5 @@ redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0 redis.connection.password= + +rule.queue.type=memory diff --git a/dao/src/test/resources/cassandra-test.properties b/dao/src/test/resources/cassandra-test.properties index 737687f053..cf07b22e42 100644 --- a/dao/src/test/resources/cassandra-test.properties +++ b/dao/src/test/resources/cassandra-test.properties @@ -46,6 +46,8 @@ cassandra.query.default_fetch_size=2000 cassandra.query.ts_key_value_partitioning=HOURS +cassandra.query.ts_key_value_ttl=0 + cassandra.query.max_limit_per_request=1000 cassandra.query.buffer_size=100000 cassandra.query.concurrent_limit=1000 diff --git a/dao/src/test/resources/cassandra-test.yaml b/dao/src/test/resources/cassandra-test.yaml index 6463f64176..e60f248d95 100644 --- a/dao/src/test/resources/cassandra-test.yaml +++ b/dao/src/test/resources/cassandra-test.yaml @@ -103,6 +103,8 @@ commitlog_directory: target/embeddedCassandra/commitlog hints_directory: target/embeddedCassandra/hints +cdc_raw_directory: target/embeddedCassandra/cdc + # policy for data disk failures: # stop: shut down gossip and Thrift, leaving the node effectively dead, but # can still be inspected via JMX. diff --git a/dao/src/test/resources/cassandra/system-test.cql b/dao/src/test/resources/cassandra/system-test.cql index da5d1f1154..de4e325745 100644 --- a/dao/src/test/resources/cassandra/system-test.cql +++ b/dao/src/test/resources/cassandra/system-test.cql @@ -1,2 +1,27 @@ -TRUNCATE thingsboard.plugin; -TRUNCATE thingsboard.rule; \ No newline at end of file +TRUNCATE thingsboard.rule_chain; +TRUNCATE thingsboard.rule_node; + +-- msg_queue dataset + +INSERT INTO thingsboard.msg_queue (node_id, cluster_partition, ts_partition, ts, msg) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 200, 201, null); +INSERT INTO thingsboard.msg_queue (node_id, cluster_partition, ts_partition, ts, msg) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 200, 202, null); +INSERT INTO thingsboard.msg_queue (node_id, cluster_partition, ts_partition, ts, msg) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 300, 301, null); + +-- ack_queue dataset +INSERT INTO thingsboard.msg_ack_queue (node_id, cluster_partition, ts_partition, msg_id) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 300, bebaeb60-1888-11e8-bf21-65b5d5335ba9); +INSERT INTO thingsboard.msg_ack_queue (node_id, cluster_partition, ts_partition, msg_id) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 300, 12baeb60-1888-11e8-bf21-65b5d5335ba9); +INSERT INTO thingsboard.msg_ack_queue (node_id, cluster_partition, ts_partition, msg_id) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 200, 32baeb60-1888-11e8-bf21-65b5d5335ba9); + +-- processed partition dataset +INSERT INTO thingsboard.processed_msg_partitions (node_id, cluster_partition, ts_partition) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 100); +INSERT INTO thingsboard.processed_msg_partitions (node_id, cluster_partition, ts_partition) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 101, 777); +INSERT INTO thingsboard.processed_msg_partitions (node_id, cluster_partition, ts_partition) + VALUES (055eee50-1883-11e8-b380-65b5d5335ba9, 202, 200); \ No newline at end of file diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index 482c6e7ed3..e37e228b40 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/dao/src/test/resources/nosql-test.properties @@ -1 +1,6 @@ -database.type=cassandra \ No newline at end of file +database.type=cassandra + +cassandra.queue.partitioning=HOURS +cassandra.queue.ack.ttl=3600 +cassandra.queue.msg.ttl=3600 +cassandra.queue.partitions.ttl=3600 \ No newline at end of file diff --git a/dao/src/test/resources/sql/drop-all-tables.sql b/dao/src/test/resources/sql/drop-all-tables.sql index dfdc90fc35..23b6a56a47 100644 --- a/dao/src/test/resources/sql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/drop-all-tables.sql @@ -9,13 +9,13 @@ DROP TABLE IF EXISTS dashboard; DROP TABLE IF EXISTS device; DROP TABLE IF EXISTS device_credentials; DROP TABLE IF EXISTS event; -DROP TABLE IF EXISTS plugin; DROP TABLE IF EXISTS relation; -DROP TABLE IF EXISTS rule; DROP TABLE IF EXISTS tb_user; DROP TABLE IF EXISTS tenant; DROP TABLE IF EXISTS ts_kv; DROP TABLE IF EXISTS ts_kv_latest; DROP TABLE IF EXISTS user_credentials; DROP TABLE IF EXISTS widget_type; -DROP TABLE IF EXISTS widgets_bundle; \ No newline at end of file +DROP TABLE IF EXISTS widgets_bundle; +DROP TABLE IF EXISTS rule_node; +DROP TABLE IF EXISTS rule_chain; \ No newline at end of file diff --git a/dao/src/test/resources/sql/system-test.sql b/dao/src/test/resources/sql/system-test.sql index 57d49ddad9..e59afd914c 100644 --- a/dao/src/test/resources/sql/system-test.sql +++ b/dao/src/test/resources/sql/system-test.sql @@ -1,2 +1,2 @@ -TRUNCATE TABLE plugin; -TRUNCATE TABLE rule; \ No newline at end of file +TRUNCATE TABLE rule_node; +TRUNCATE TABLE rule_chain; \ No newline at end of file diff --git a/docker/cassandra-setup/Makefile b/docker/cassandra-setup/Makefile index fbe3ff9c2d..c572987b37 100644 --- a/docker/cassandra-setup/Makefile +++ b/docker/cassandra-setup/Makefile @@ -1,4 +1,4 @@ -VERSION=1.4.0 +VERSION=2.0.0 PROJECT=thingsboard APP=cassandra-setup diff --git a/docker/cassandra/Makefile b/docker/cassandra/Makefile index adc9c7e968..e84be0f9c8 100644 --- a/docker/cassandra/Makefile +++ b/docker/cassandra/Makefile @@ -1,4 +1,4 @@ -VERSION=1.4.0 +VERSION=2.0.0 PROJECT=thingsboard APP=cassandra diff --git a/docker/cluster-mode-thirdparty.yml b/docker/cluster-mode-thirdparty.yml new file mode 100644 index 0000000000..3aa5bb5959 --- /dev/null +++ b/docker/cluster-mode-thirdparty.yml @@ -0,0 +1,45 @@ +# +# Copyright © 2016-2018 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. +# + +version: '3.3' +services: + zookeeper: + image: wurstmeister/zookeeper + networks: + - core + ports: + - "2181:2181" + + cassandra: + image: cassandra:3.11.2 + networks: + - core + ports: + - "7199:7199" + - "9160:9160" + - "9042:9042" + + redis: + image: redis:4.0 + networks: + - core + command: redis-server --maxclients 2000 + ports: + - "6379:6379" + +networks: + core: + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6662a8f95f..28323b7bf9 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ version: '2' services: tb: - image: "thingsboard/application:1.4.0" + image: "thingsboard/application:2.0.0" ports: - "8080:8080" - "1883:1883" diff --git a/docker/k8s/cassandra-setup.yaml b/docker/k8s/cassandra-setup.yaml index bf50ba9afe..2f07238d99 100644 --- a/docker/k8s/cassandra-setup.yaml +++ b/docker/k8s/cassandra-setup.yaml @@ -22,7 +22,7 @@ spec: containers: - name: cassandra-setup imagePullPolicy: Always - image: thingsboard/cassandra-setup:1.4.0 + image: thingsboard/cassandra-setup:2.0.0 env: - name: ADD_DEMO_DATA value: "true" diff --git a/docker/k8s/cassandra.yaml b/docker/k8s/cassandra.yaml index 15d0e55eb1..d6264ae9a5 100644 --- a/docker/k8s/cassandra.yaml +++ b/docker/k8s/cassandra.yaml @@ -54,7 +54,7 @@ spec: topologyKey: "kubernetes.io/hostname" containers: - name: cassandra - image: thingsboard/cassandra:1.4.0 + image: thingsboard/cassandra:2.0.0 imagePullPolicy: Always ports: - containerPort: 7000 diff --git a/docker/k8s/tb.yaml b/docker/k8s/tb.yaml index 1d94cb11ac..2a4e6f8ffb 100644 --- a/docker/k8s/tb.yaml +++ b/docker/k8s/tb.yaml @@ -84,7 +84,7 @@ spec: containers: - name: tb imagePullPolicy: Always - image: thingsboard/application:1.4.0 + image: thingsboard/application:2.0.0 ports: - containerPort: 8080 name: ui diff --git a/docker/k8s/zookeeper.yaml b/docker/k8s/zookeeper.yaml index bea0833a0a..b5c6dce54c 100644 --- a/docker/k8s/zookeeper.yaml +++ b/docker/k8s/zookeeper.yaml @@ -87,7 +87,7 @@ spec: containers: - name: zk imagePullPolicy: Always - image: thingsboard/zk:1.4.0 + image: thingsboard/zk:2.0.0 ports: - containerPort: 2181 name: client diff --git a/docker/tb/Makefile b/docker/tb/Makefile index 05d3f4d2e4..cfff5a429e 100644 --- a/docker/tb/Makefile +++ b/docker/tb/Makefile @@ -1,4 +1,4 @@ -VERSION=1.4.0 +VERSION=2.0.0 PROJECT=thingsboard APP=application diff --git a/docker/zookeeper/Makefile b/docker/zookeeper/Makefile index 3386ff8586..52b80fb60c 100644 --- a/docker/zookeeper/Makefile +++ b/docker/zookeeper/Makefile @@ -1,4 +1,4 @@ -VERSION=1.4.0 +VERSION=2.0.0 PROJECT=thingsboard APP=zk diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Action.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Action.java deleted file mode 100644 index 5f7577f512..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Action.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.component; - -import org.thingsboard.server.common.data.plugin.ComponentScope; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Andrew Shvayka - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Action { - - String name(); - - ComponentScope scope() default ComponentScope.TENANT; - - String descriptor() default "EmptyJsonDescriptor.json"; - - Class configuration() default EmptyComponentConfiguration.class; - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Filter.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Filter.java deleted file mode 100644 index aaf7456581..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Filter.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.component; - -import org.thingsboard.server.common.data.plugin.ComponentScope; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Andrew Shvayka - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Filter { - - String name(); - - ComponentScope scope() default ComponentScope.TENANT; - - String descriptor() default "EmptyJsonDescriptor.json"; - - Class configuration() default EmptyComponentConfiguration.class; - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Processor.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Processor.java deleted file mode 100644 index 32a958f821..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/component/Processor.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.component; - -import org.thingsboard.server.common.data.plugin.ComponentScope; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author Andrew Shvayka - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Processor { - - String name(); - - ComponentScope scope() default ComponentScope.TENANT; - - String descriptor() default "EmptyJsonDescriptor.json"; - - Class configuration() default EmptyComponentConfiguration.class; - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configurable.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configurable.java deleted file mode 100644 index 87b148dc56..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configurable.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.configuration; - -public interface Configurable { - - Class getConfigurationClass(); - - void validate(C configuration) throws ConfigurationValidationException; - - void configure(C configuration); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configuration.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configuration.java deleted file mode 100644 index efaec930eb..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/configuration/Configuration.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.configuration; - -import java.io.Serializable; - -public interface Configuration extends Serializable { - - // TODO: Figure out how to define and visualize configurations - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/AbstractPlugin.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/AbstractPlugin.java deleted file mode 100644 index 34f4ce794a..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/AbstractPlugin.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.extensions.api.plugins.handlers.*; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -/** - * @author Andrew Shvayka - */ -public abstract class AbstractPlugin implements Plugin { - - @Override - public void process(PluginContext ctx, PluginWebsocketMsg wsMsg) { - getWebsocketMsgHandler().process(ctx, wsMsg); - } - - @Override - public void process(PluginContext ctx, PluginRestMsg msg) { - getRestMsgHandler().process(ctx, msg); - } - - @Override - public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg msg) throws RuleException { - getRuleMsgHandler().process(ctx, tenantId, ruleId, msg); - } - - @Override - public void process(PluginContext ctx, RpcMsg msg) { - getRpcMsgHandler().process(ctx, msg); - } - - @Override - public void process(PluginContext ctx, FromDeviceRpcResponse msg) { - throw new IllegalStateException("Device RPC messages is not handled in current plugin!"); - } - - @Override - public void process(PluginContext ctx, TimeoutMsg msg) { - throw new IllegalStateException("Timeouts is not handled in current plugin!"); - } - - @Override - public void onServerAdded(PluginContext ctx, ServerAddress server) { - } - - @Override - public void onServerRemoved(PluginContext ctx, ServerAddress server) { - } - - protected RuleMsgHandler getRuleMsgHandler() { - return new DefaultRuleMsgHandler(); - } - - protected RestMsgHandler getRestMsgHandler() { - return new DefaultRestMsgHandler(); - } - - protected WebsocketMsgHandler getWebsocketMsgHandler() { - return new DefaultWebsocketMsgHandler(); - } - - protected RpcMsgHandler getRpcMsgHandler() { - return new DefaultRpcMsgHandler(); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/Plugin.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/Plugin.java deleted file mode 100644 index a41f3c14e2..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/Plugin.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.extensions.api.component.ConfigurableComponent; -import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -public interface Plugin extends ConfigurableComponent { - - void process(PluginContext ctx, PluginWebsocketMsg wsMsg); - - void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg msg) throws RuleException; - - void process(PluginContext ctx, PluginRestMsg msg); - - void process(PluginContext ctx, RpcMsg msg); - - void process(PluginContext ctx, FromDeviceRpcResponse msg); - - void process(PluginContext ctx, TimeoutMsg msg); - - void onServerAdded(PluginContext ctx, ServerAddress server); - - void onServerRemoved(PluginContext ctx, ServerAddress server); - - void resume(PluginContext ctx); - - void suspend(PluginContext ctx); - - void stop(PluginContext ctx); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginAction.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginAction.java deleted file mode 100644 index 3c19571560..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginAction.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.component.ConfigurableComponent; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.rules.RuleContext; -import org.thingsboard.server.extensions.api.rules.RuleLifecycleComponent; -import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData; - -import java.util.Optional; - -public interface PluginAction extends ConfigurableComponent, RuleLifecycleComponent { - - Optional convert(RuleContext ctx, ToDeviceActorMsg toDeviceActorMsg, RuleProcessingMetaData deviceMsgMd); - - Optional convert(PluginToRuleMsg response); - - boolean isOneWayAction(); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginApiCallSecurityContext.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginApiCallSecurityContext.java deleted file mode 100644 index 2bbc26159b..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginApiCallSecurityContext.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -import org.thingsboard.server.common.data.id.*; - -import java.io.Serializable; - -public final class PluginApiCallSecurityContext implements Serializable { - - private static final long serialVersionUID = 1L; - - private final TenantId pluginTenantId; - private final PluginId pluginId; - private final TenantId tenantId; - private final CustomerId customerId; - private final UserId userId; - private final String userName; - - public PluginApiCallSecurityContext(TenantId pluginTenantId, PluginId pluginId, TenantId tenantId, CustomerId customerId, - UserId userId, String userName) { - super(); - this.pluginTenantId = pluginTenantId; - this.pluginId = pluginId; - this.tenantId = tenantId; - this.customerId = customerId; - this.userId = userId; - this.userName = userName; - } - - public TenantId getPluginTenantId(){ - return pluginTenantId; - } - - public PluginId getPluginId() { - return pluginId; - } - - public boolean isSystemAdmin() { - return tenantId == null || EntityId.NULL_UUID.equals(tenantId.getId()); - } - - public boolean isTenantAdmin() { - return !isSystemAdmin() && (customerId == null || EntityId.NULL_UUID.equals(customerId.getId())); - } - - public boolean isCustomerUser() { - return !isSystemAdmin() && !isTenantAdmin(); - } - - public TenantId getTenantId() { - return tenantId; - } - - public CustomerId getCustomerId() { - return customerId; - } - - public UserId getUserId() { - return userId; - } - - public String getUserName() { - return userName; - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginConstants.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginConstants.java deleted file mode 100644 index ac9600a6a5..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -/** - * @author Andrew Shvayka - */ -public class PluginConstants { - public static final String PLUGIN_URL_PREFIX = "/api/plugins"; -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java deleted file mode 100644 index 37f4c4a383..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.kv.TsKvQuery; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.msg.cluster.ServerAddress; -import org.thingsboard.server.extensions.api.plugins.msg.*; -import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -public interface PluginContext { - - PluginId getPluginId(); - - void reply(PluginToRuleMsg msg); - - void checkAccess(DeviceId deviceId, PluginCallback callback); - - Optional getSecurityCtx(); - - void persistError(String method, Exception e); - - /* - Device RPC API - */ - - Optional resolve(EntityId entityId); - - void getDevice(DeviceId deviceId, PluginCallback pluginCallback); - - void sendRpcRequest(ToDeviceRpcRequest msg); - - void scheduleTimeoutMsg(TimeoutMsg timeoutMsg); - - void logRpcRequest(PluginApiCallSecurityContext ctx, DeviceId deviceId, ToDeviceRpcRequestBody body, boolean oneWay, Optional rpcError, Exception e); - - /* - Websocket API - */ - - void send(PluginWebsocketMsg wsMsg) throws IOException; - - void close(PluginWebsocketSessionRef sessionRef) throws IOException; - - /* - Plugin RPC API - */ - - void sendPluginRpcMsg(RpcMsg msg); - - /* - Timeseries API - */ - - - void saveTsData(EntityId entityId, TsKvEntry entry, PluginCallback callback); - - void saveTsData(EntityId entityId, List entries, PluginCallback callback); - - void saveTsData(EntityId deviceId, List entries, long ttl, PluginCallback pluginCallback); - - void loadTimeseries(EntityId entityId, List queries, PluginCallback> callback); - - void loadLatestTimeseries(EntityId entityId, Collection keys, PluginCallback> callback); - - void loadLatestTimeseries(EntityId entityId, PluginCallback> callback); - - /* - Attributes API - */ - - void logAttributesUpdated(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List attributes, Exception e); - - void logAttributesDeleted(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e); - - void logAttributesRead(PluginApiCallSecurityContext ctx, EntityId entityId, String attributeType, List keys, Exception e); - - void saveAttributes(TenantId tenantId, EntityId entityId, String attributeType, List attributes, PluginCallback callback); - - void removeAttributes(TenantId tenantId, EntityId entityId, String scope, List attributeKeys, PluginCallback callback); - - void loadAttribute(EntityId entityId, String attributeType, String attributeKey, PluginCallback> callback); - - void loadAttributes(EntityId entityId, String attributeType, Collection attributeKeys, PluginCallback> callback); - - void loadAttributes(EntityId entityId, String attributeType, PluginCallback> callback); - - void loadAttributes(EntityId entityId, Collection attributeTypes, PluginCallback> callback); - - void loadAttributes(EntityId entityId, Collection attributeTypes, Collection attributeKeys, PluginCallback> callback); - - void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback> callback); - - - /* - * Relations API - * */ - - ListenableFuture> findByFromAndType(EntityId from, String relationType); - - ListenableFuture> findByToAndType(EntityId from, String relationType); -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginException.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginException.java deleted file mode 100644 index a7856f7b52..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginException.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -public class PluginException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public PluginException(String msg, Exception e) { - super(msg, e); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginInitializationException.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginInitializationException.java deleted file mode 100644 index cbae159f07..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginInitializationException.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins; - -public class PluginInitializationException extends PluginException { - - private static final long serialVersionUID = 1L; - - public PluginInitializationException(String msg, Exception e) { - super(msg, e); - } - - public PluginInitializationException(String msg) { - super(msg, null); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRestMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRestMsgHandler.java deleted file mode 100644 index dec50e4ff9..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRestMsgHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpMethod; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; - -import javax.servlet.ServletException; - -/** - * @author Andrew Shvayka - */ -@Slf4j -public class DefaultRestMsgHandler implements RestMsgHandler { - - protected final ObjectMapper jsonMapper = new ObjectMapper(); - - @Override - public void process(PluginContext ctx, PluginRestMsg msg) { - try { - log.debug("[{}] Processing REST msg: {}", ctx.getPluginId(), msg); - HttpMethod method = msg.getRequest().getMethod(); - switch (method) { - case GET: - handleHttpGetRequest(ctx, msg); - break; - case POST: - handleHttpPostRequest(ctx, msg); - break; - case DELETE: - handleHttpDeleteRequest(ctx, msg); - break; - default: - msg.getResponseHolder().setErrorResult(new HttpRequestMethodNotSupportedException(method.name())); - } - log.debug("[{}] Processed REST msg.", ctx.getPluginId()); - } catch (Exception e) { - log.warn("[{}] Exception during REST msg processing: {}", ctx.getPluginId(), e.getMessage(), e); - msg.getResponseHolder().setErrorResult(e); - } - } - - protected void handleHttpGetRequest(PluginContext ctx, PluginRestMsg msg) throws ServletException { - msg.getResponseHolder().setErrorResult(new HttpRequestMethodNotSupportedException(HttpMethod.GET.name())); - } - - protected void handleHttpPostRequest(PluginContext ctx, PluginRestMsg msg) throws ServletException { - msg.getResponseHolder().setErrorResult(new HttpRequestMethodNotSupportedException(HttpMethod.POST.name())); - } - - protected void handleHttpDeleteRequest(PluginContext ctx, PluginRestMsg msg) throws ServletException { - msg.getResponseHolder().setErrorResult(new HttpRequestMethodNotSupportedException(HttpMethod.DELETE.name())); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRuleMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRuleMsgHandler.java deleted file mode 100644 index 27b476d0c0..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultRuleMsgHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.session.MsgType; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.msg.GetAttributesRequestRuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.TelemetryUploadRequestRuleToPluginMsg; -import org.thingsboard.server.extensions.api.plugins.msg.UpdateAttributesRequestRuleToPluginMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -/** - * @author Andrew Shvayka - */ -@Slf4j -public class DefaultRuleMsgHandler implements RuleMsgHandler { - - @Override - public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg msg) throws RuleException { - if (msg instanceof TelemetryUploadRequestRuleToPluginMsg) { - handleTelemetryUploadRequest(ctx, tenantId, ruleId, (TelemetryUploadRequestRuleToPluginMsg) msg); - } else if (msg instanceof UpdateAttributesRequestRuleToPluginMsg) { - handleUpdateAttributesRequest(ctx, tenantId, ruleId, (UpdateAttributesRequestRuleToPluginMsg) msg); - } else if (msg instanceof GetAttributesRequestRuleToPluginMsg) { - handleGetAttributesRequest(ctx, tenantId, ruleId, (GetAttributesRequestRuleToPluginMsg) msg); - } - //TODO: handle subscriptions to attribute updates. - } - - protected void handleGetAttributesRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, GetAttributesRequestRuleToPluginMsg msg) { - msgTypeNotSupported(msg.getPayload().getMsgType()); - } - - protected void handleUpdateAttributesRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, UpdateAttributesRequestRuleToPluginMsg msg) { - msgTypeNotSupported(msg.getPayload().getMsgType()); - } - - protected void handleTelemetryUploadRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, TelemetryUploadRequestRuleToPluginMsg msg) { - msgTypeNotSupported(msg.getPayload().getMsgType()); - } - - private void msgTypeNotSupported(MsgType msgType) { - throw new RuntimeException("Not supported msg type: " + msgType + "!"); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultWebsocketMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultWebsocketMsgHandler.java deleted file mode 100644 index 53bb31a8aa..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/DefaultWebsocketMsgHandler.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent; -import org.thingsboard.server.extensions.api.plugins.ws.WsSessionMetaData; -import org.thingsboard.server.extensions.api.plugins.ws.msg.*; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -/** - * @author Andrew Shvayka - */ -@Slf4j -public class DefaultWebsocketMsgHandler implements WebsocketMsgHandler { - - public static final String PROCESSING_MSG = "[{}] Processing: {}"; - protected final ObjectMapper jsonMapper = new ObjectMapper(); - - protected final Map wsSessionsMap = new HashMap<>(); - - @Override - public void process(PluginContext ctx, PluginWebsocketMsg wsMsg) { - PluginWebsocketSessionRef sessionRef = wsMsg.getSessionRef(); - if (log.isTraceEnabled()) { - log.trace(PROCESSING_MSG, sessionRef.getSessionId(), wsMsg); - } else { - log.debug(PROCESSING_MSG, sessionRef.getSessionId(), wsMsg.getClass().getSimpleName()); - } - if (wsMsg instanceof SessionEventPluginWebSocketMsg) { - handleWebSocketSessionEvent(ctx, sessionRef, (SessionEventPluginWebSocketMsg) wsMsg); - } else if (wsMsg instanceof TextPluginWebSocketMsg || wsMsg instanceof BinaryPluginWebSocketMsg) { - handleWebSocketMsg(ctx, sessionRef, wsMsg); - } else if (wsMsg instanceof PongPluginWebsocketMsg) { - handleWebSocketPongEvent(ctx, sessionRef); - } - } - - protected void handleWebSocketMsg(PluginContext ctx, PluginWebsocketSessionRef sessionRef, PluginWebsocketMsg wsMsg) { - throw new RuntimeException("Web-sockets are not supported by current plugin!"); - } - - protected void cleanupWebSocketSession(PluginContext ctx, String sessionId) { - //Do nothing - } - - protected void handleWebSocketSessionEvent(PluginContext ctx, PluginWebsocketSessionRef sessionRef, SessionEventPluginWebSocketMsg wsMsg) { - String sessionId = sessionRef.getSessionId(); - SessionEvent event = wsMsg.getPayload(); - log.debug(PROCESSING_MSG, sessionId, event); - switch (event.getEventType()) { - case ESTABLISHED: - wsSessionsMap.put(sessionId, new WsSessionMetaData(sessionRef)); - break; - case ERROR: - log.debug("[{}] Unknown websocket session error: {}. ", sessionId, event.getError().orElse(null)); - break; - case CLOSED: - wsSessionsMap.remove(sessionId); - cleanupWebSocketSession(ctx, sessionId); - break; - } - } - - protected void handleWebSocketPongEvent(PluginContext ctx, PluginWebsocketSessionRef sessionRef) { - String sessionId = sessionRef.getSessionId(); - WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); - if (sessionMD != null) { - log.debug("[{}] Updating session metadata: {}", sessionId, sessionRef); - sessionMD.setSessionRef(sessionRef); - sessionMD.setLastActivityTime(System.currentTimeMillis()); - } - } - - public void clear(PluginContext ctx) { - wsSessionsMap.values().forEach(v -> { - try { - ctx.close(v.getSessionRef()); - } catch (IOException e) { - log.debug("[{}] Failed to close session: {}", v.getSessionRef().getSessionId(), e.getMessage(), e); - } - }); - wsSessionsMap.clear(); - } -} \ No newline at end of file diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RestMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RestMsgHandler.java deleted file mode 100644 index 19dde6cd7c..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RestMsgHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; - -/** - * @author Andrew Shvayka - */ -public interface RestMsgHandler { - - void process(PluginContext ctx, PluginRestMsg msg); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RuleMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RuleMsgHandler.java deleted file mode 100644 index 7357578527..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/RuleMsgHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.rules.RuleException; - -/** - * @author Andrew Shvayka - */ -public interface RuleMsgHandler { - - void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg msg) throws RuleException; - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/WebsocketMsgHandler.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/WebsocketMsgHandler.java deleted file mode 100644 index efda4e9842..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/handlers/WebsocketMsgHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.handlers; - -import org.thingsboard.server.extensions.api.plugins.PluginContext; -import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; - -/** - * @author Andrew Shvayka - */ -public interface WebsocketMsgHandler { - - void process(PluginContext ctx, PluginWebsocketMsg wsMsg); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractPluginToRuleMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractPluginToRuleMsg.java deleted file mode 100644 index 39275c8638..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractPluginToRuleMsg.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; - -import java.io.Serializable; -import java.util.UUID; - -public class AbstractPluginToRuleMsg implements PluginToRuleMsg { - - private static final long serialVersionUID = 1L; - - private final UUID uid; - private final TenantId tenantId; - private final RuleId ruleId; - private final T payload; - - public AbstractPluginToRuleMsg(UUID uid, TenantId tenantId, RuleId ruleId, T payload) { - super(); - this.uid = uid; - this.tenantId = tenantId; - this.ruleId = ruleId; - this.payload = payload; - } - - @Override - public UUID getUid() { - return uid; - } - - @Override - public TenantId getTenantId() { - return tenantId; - } - - @Override - public T getPayload() { - return payload; - } - - @Override - public RuleId getRuleId() { - return ruleId; - } - - - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractRuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractRuleToPluginMsg.java deleted file mode 100644 index d2463153fa..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/AbstractRuleToPluginMsg.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; - -import java.io.Serializable; -import java.util.UUID; - -public abstract class AbstractRuleToPluginMsg implements RuleToPluginMsg { - - private static final long serialVersionUID = 1L; - - private final UUID uid; - private final TenantId tenantId; - private final CustomerId customerId; - private final DeviceId deviceId; - private final T payload; - - public AbstractRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, T payload) { - super(); - this.uid = UUID.randomUUID(); - this.tenantId = tenantId; - this.customerId = customerId; - this.deviceId = deviceId; - this.payload = payload; - } - - @Override - public UUID getUid() { - return uid; - } - - public TenantId getTenantId() { - return tenantId; - } - - public CustomerId getCustomerId() { - return customerId; - } - - @Override - public DeviceId getDeviceId() { - return deviceId; - } - - public T getPayload() { - return payload; - } - - @Override - public String toString() { - return "AbstractRuleToPluginMsg [uid=" + uid + ", tenantId=" + tenantId + ", customerId=" + customerId - + ", deviceId=" + deviceId + ", payload=" + payload + "]"; - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetRequestRuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetRequestRuleToPluginMsg.java deleted file mode 100644 index f6ea02180c..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/GetRequestRuleToPluginMsg.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; - -public class GetRequestRuleToPluginMsg extends AbstractRuleToPluginMsg{ - - private static final long serialVersionUID = 1L; - - public GetRequestRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, - String[] payload) { - super(tenantId, customerId, deviceId, payload); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/PluginToRuleMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/PluginToRuleMsg.java deleted file mode 100644 index 7871ea3001..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/PluginToRuleMsg.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg; - -import java.io.Serializable; -import java.util.UUID; - -/** - * The basic interface for messages that are sent from particular plugin to rule - * instance - * - * @author ashvayka - * @see RuleToPluginMsg - * - */ -public interface PluginToRuleMsg extends ToRuleActorMsg, Serializable { - - /** - * Returns the unique identifier of the message - * - * @return unique identifier of the message. - */ - UUID getUid(); - - /** - * Returns the unique identifier of the tenant that owns the rule - * - * @return unique identifier of the tenant that owns the rule. - * - */ - TenantId getTenantId(); - - /** - * Returns the unique identifier of the rule - * - * @return unique identifier of the rule. - */ - RuleId getRuleId(); - - /** - * Returns the serializable message payload. - * - * @return the serializable message payload. - */ - V getPayload(); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcRequestRuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcRequestRuleToPluginMsg.java deleted file mode 100644 index 17b42a46ae..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RpcRequestRuleToPluginMsg.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg; - -public class RpcRequestRuleToPluginMsg extends AbstractRuleToPluginMsg { - - private static final long serialVersionUID = 1L; - - public RpcRequestRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, ToServerRpcRequestMsg payload) { - super(tenantId, customerId, deviceId, payload); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RuleToPluginMsg.java deleted file mode 100644 index c4e7465373..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/RuleToPluginMsg.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; - -import java.io.Serializable; -import java.util.UUID; - -/** - * The basic interface for messages that are sent from particular rule to plugin - * instance - * - * @author ashvayka - * - */ -public interface RuleToPluginMsg extends Serializable { - - /** - * Returns the unique identifier of the message - * - * @return unique identifier of the message. - */ - UUID getUid(); - - - /** - * Returns the unique identifier of the device that send the message - * - * @return unique identifier of the device. - */ - DeviceId getDeviceId(); - - /** - * Returns the unique identifier of the customer that owns the device - * - * @return unique identifier of the device. - */ - CustomerId getCustomerId(); - - /** - * Returns the serializable message payload. - * - * @return the serializable message payload. - */ - V getPayload(); -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TelemetryUploadRequestRuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TelemetryUploadRequestRuleToPluginMsg.java deleted file mode 100644 index 6d560f18c6..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/TelemetryUploadRequestRuleToPluginMsg.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; - -public class TelemetryUploadRequestRuleToPluginMsg extends AbstractRuleToPluginMsg { - - private static final long serialVersionUID = 1L; - private final long ttl; - - public TelemetryUploadRequestRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, TelemetryUploadRequest payload, long ttl) { - super(tenantId, customerId, deviceId, payload); - this.ttl = ttl; - } - - public long getTtl() { - return ttl; - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginActorMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginActorMsg.java deleted file mode 100644 index 32804d4dc3..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToPluginActorMsg.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.aware.PluginAwareMsg; - -public interface ToPluginActorMsg extends PluginAwareMsg { - - TenantId getPluginTenantId(); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/UpdateAttributesRequestRuleToPluginMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/UpdateAttributesRequestRuleToPluginMsg.java deleted file mode 100644 index 991ad8d02d..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/UpdateAttributesRequestRuleToPluginMsg.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.msg; - -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.core.UpdateAttributesRequest; - -public class UpdateAttributesRequestRuleToPluginMsg extends AbstractRuleToPluginMsg { - - private static final long serialVersionUID = 1L; - - public UpdateAttributesRequestRuleToPluginMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, UpdateAttributesRequest payload) { - super(tenantId, customerId, deviceId, payload); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/BasicPluginRestMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/BasicPluginRestMsg.java deleted file mode 100644 index 5619ebe6f1..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/BasicPluginRestMsg.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.rest; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; - -@SuppressWarnings("rawtypes") -public class BasicPluginRestMsg implements PluginRestMsg { - - private final PluginApiCallSecurityContext securityCtx; - private final RestRequest request; - private final DeferredResult responseHolder; - - public BasicPluginRestMsg(PluginApiCallSecurityContext securityCtx, RestRequest request, - DeferredResult responseHolder) { - super(); - this.securityCtx = securityCtx; - this.request = request; - this.responseHolder = responseHolder; - } - - @Override - public PluginApiCallSecurityContext getSecurityCtx() { - return securityCtx; - } - - @Override - public RestRequest getRequest() { - return request; - } - - @Override - public DeferredResult getResponseHolder() { - return responseHolder; - } - - @Override - public PluginId getPluginId() { - return securityCtx.getPluginId(); - } - - @Override - public TenantId getPluginTenantId() { - return securityCtx.getPluginTenantId(); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/PluginRestMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/PluginRestMsg.java deleted file mode 100644 index 07c8245de5..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/PluginRestMsg.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.rest; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.async.DeferredResult; -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; - -@SuppressWarnings("rawtypes") -public interface PluginRestMsg extends ToPluginActorMsg { - - RestRequest getRequest(); - - DeferredResult getResponseHolder(); - - PluginApiCallSecurityContext getSecurityCtx(); - - @Override - default PluginId getPluginId() { - return getSecurityCtx().getPluginId(); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/RestRequest.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/RestRequest.java deleted file mode 100644 index 8807c85a69..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/rest/RestRequest.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.rest; - -import lombok.Data; -import org.springframework.http.HttpMethod; -import org.springframework.http.RequestEntity; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.MissingServletRequestParameterException; -import org.thingsboard.server.extensions.api.plugins.PluginConstants; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import java.nio.charset.Charset; -import java.util.Optional; -import java.util.function.Function; - -@Data -public class RestRequest { - private static final Charset UTF8 = Charset.forName("UTF-8"); - private final RequestEntity requestEntity; - private final HttpServletRequest request; - - public HttpMethod getMethod() { - return requestEntity.getMethod(); - } - - public String getRequestBody() { - return new String(requestEntity.getBody(), UTF8); - } - - public String[] getPathParams() { - String requestUrl = request.getRequestURL().toString(); - int index = requestUrl.indexOf(PluginConstants.PLUGIN_URL_PREFIX); - String[] pathParams = requestUrl.substring(index + PluginConstants.PLUGIN_URL_PREFIX.length()).split("/"); - String[] result = new String[pathParams.length - 2]; - System.arraycopy(pathParams, 2, result, 0, result.length); - return result; - } - - public String getParameter(String paramName) throws ServletException { - return getParameter(paramName, null); - } - - public String getParameter(String paramName, String defaultValue) throws ServletException { - String paramValue = request.getParameter(paramName); - if (StringUtils.isEmpty(paramValue)) { - if (defaultValue == null) { - throw new MissingServletRequestParameterException(paramName, "String"); - } else { - return defaultValue; - } - } else { - return paramValue; - } - } - - public Optional getLongParamValue(String paramName) { - return getParamValue(paramName, s -> Long.valueOf(s)); - } - - public Optional getIntParamValue(String paramName) { - return getParamValue(paramName, s -> Integer.valueOf(s)); - } - - public Optional getParamValue(String paramName, Function function) { - String paramValue = request.getParameter(paramName); - if (paramValue != null) { - return Optional.of(function.apply(paramValue)); - } else { - return Optional.empty(); - } - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/BasicPluginWebsocketSessionRef.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/BasicPluginWebsocketSessionRef.java deleted file mode 100644 index a3247a8b80..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/BasicPluginWebsocketSessionRef.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws; - -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; - -import java.net.InetSocketAddress; -import java.net.URI; -import java.util.Map; - -public class BasicPluginWebsocketSessionRef implements PluginWebsocketSessionRef { - - private static final long serialVersionUID = 1L; - - private final String sessionId; - private final PluginApiCallSecurityContext securityCtx; - private final URI uri; - private final transient Map attributes; - private final InetSocketAddress localAddress; - private final InetSocketAddress remoteAddress; - - public BasicPluginWebsocketSessionRef(String sessionId, PluginApiCallSecurityContext securityCtx, URI uri, Map attributes, - InetSocketAddress localAddress, InetSocketAddress remoteAddress) { - super(); - this.sessionId = sessionId; - this.securityCtx = securityCtx; - this.uri = uri; - this.attributes = attributes; - this.localAddress = localAddress; - this.remoteAddress = remoteAddress; - } - - public String getSessionId() { - return sessionId; - } - - public TenantId getPluginTenantId() { - return securityCtx.getPluginTenantId(); - } - - public PluginId getPluginId() { - return securityCtx.getPluginId(); - } - - public URI getUri() { - return uri; - } - - public Map getAttributes() { - return attributes; - } - - public InetSocketAddress getLocalAddress() { - return localAddress; - } - - public InetSocketAddress getRemoteAddress() { - return remoteAddress; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BasicPluginWebsocketSessionRef other = (BasicPluginWebsocketSessionRef) obj; - if (sessionId == null) { - if (other.sessionId != null) - return false; - } else if (!sessionId.equals(other.sessionId)) - return false; - return true; - } - - @Override - public String toString() { - return "BasicPluginWebsocketSessionRef [sessionId=" + sessionId + ", pluginId=" + getPluginId() + "]"; - } - - @Override - public PluginApiCallSecurityContext getSecurityCtx() { - return securityCtx; - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/PluginWebsocketSessionRef.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/PluginWebsocketSessionRef.java deleted file mode 100644 index 08716120c0..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/PluginWebsocketSessionRef.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws; - -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; - -import java.io.Serializable; -import java.net.InetSocketAddress; -import java.net.URI; -import java.util.Map; - -public interface PluginWebsocketSessionRef extends Serializable { - - String getSessionId(); - - TenantId getPluginTenantId(); - - PluginId getPluginId(); - - URI getUri(); - - Map getAttributes(); - - InetSocketAddress getLocalAddress(); - - InetSocketAddress getRemoteAddress(); - - PluginApiCallSecurityContext getSecurityCtx(); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/AbstractPluginWebSocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/AbstractPluginWebSocketMsg.java deleted file mode 100644 index dbcd73102f..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/AbstractPluginWebSocketMsg.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.common.data.id.PluginId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -import java.io.Serializable; - -public abstract class AbstractPluginWebSocketMsg implements PluginWebsocketMsg { - - private static final long serialVersionUID = 1L; - - private final PluginWebsocketSessionRef sessionRef; - private final transient T payload; - - AbstractPluginWebSocketMsg(PluginWebsocketSessionRef sessionRef, T payload) { - this.sessionRef = sessionRef; - this.payload = payload; - } - - public PluginWebsocketSessionRef getSessionRef() { - return sessionRef; - } - - - @Override - public TenantId getPluginTenantId(){ - return sessionRef.getPluginTenantId(); - } - - @Override - public PluginId getPluginId() { - return sessionRef.getPluginId(); - } - - @Override - public PluginApiCallSecurityContext getSecurityCtx() { - return sessionRef.getSecurityCtx(); - } - - public T getPayload() { - return payload; - } - - @Override - public String toString() { - return "AbstractPluginWebSocketMsg [sessionRef=" + sessionRef + ", payload=" + payload + "]"; - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/BinaryPluginWebSocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/BinaryPluginWebSocketMsg.java deleted file mode 100644 index c5969aa40e..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/BinaryPluginWebSocketMsg.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -import java.nio.ByteBuffer; - -public class BinaryPluginWebSocketMsg extends AbstractPluginWebSocketMsg { - - private static final long serialVersionUID = 1L; - - public BinaryPluginWebSocketMsg(PluginWebsocketSessionRef sessionRef, ByteBuffer payload) { - super(sessionRef, payload); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/EmptyPluginWebsocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/EmptyPluginWebsocketMsg.java deleted file mode 100644 index 154566317d..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/EmptyPluginWebsocketMsg.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -import java.nio.ByteBuffer; - -public class EmptyPluginWebsocketMsg extends AbstractPluginWebSocketMsg { - - private static final long serialVersionUID = 1L; - private static ByteBuffer EMPTY = ByteBuffer.wrap(new byte[0]); - - protected EmptyPluginWebsocketMsg(PluginWebsocketSessionRef sessionRef) { - super(sessionRef, EMPTY); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PingPluginWebsocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PingPluginWebsocketMsg.java deleted file mode 100644 index 3e521f8bdd..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PingPluginWebsocketMsg.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -public class PingPluginWebsocketMsg extends EmptyPluginWebsocketMsg { - - private static final long serialVersionUID = 1L; - - public PingPluginWebsocketMsg(PluginWebsocketSessionRef sessionRef) { - super(sessionRef); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PluginWebsocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PluginWebsocketMsg.java deleted file mode 100644 index b07ad8c77d..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PluginWebsocketMsg.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; -import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -import java.io.Serializable; - -public interface PluginWebsocketMsg extends ToPluginActorMsg, Serializable { - - PluginWebsocketSessionRef getSessionRef(); - - T getPayload(); - - PluginApiCallSecurityContext getSecurityCtx(); -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PongPluginWebsocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PongPluginWebsocketMsg.java deleted file mode 100644 index 3c7e9787fe..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/PongPluginWebsocketMsg.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -public class PongPluginWebsocketMsg extends EmptyPluginWebsocketMsg { - - private static final long serialVersionUID = 1L; - - public PongPluginWebsocketMsg(PluginWebsocketSessionRef sessionRef) { - super(sessionRef); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/SessionEventPluginWebSocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/SessionEventPluginWebSocketMsg.java deleted file mode 100644 index c3e23c8ca4..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/SessionEventPluginWebSocketMsg.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; -import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent; - -public class SessionEventPluginWebSocketMsg extends AbstractPluginWebSocketMsg { - - private static final long serialVersionUID = 1L; - - public SessionEventPluginWebSocketMsg(PluginWebsocketSessionRef sessionRef, SessionEvent payload) { - super(sessionRef, payload); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/TextPluginWebSocketMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/TextPluginWebSocketMsg.java deleted file mode 100644 index 1111f1fab3..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/ws/msg/TextPluginWebSocketMsg.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.plugins.ws.msg; - -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; - -public class TextPluginWebSocketMsg extends AbstractPluginWebSocketMsg { - - private static final long serialVersionUID = 1L; - - public TextPluginWebSocketMsg(PluginWebsocketSessionRef sessionRef, String payload) { - super(sessionRef, payload); - } - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleContext.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleContext.java deleted file mode 100644 index 378cb2b2eb..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleContext.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.rules; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.Event; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.extensions.api.device.DeviceMetaData; - -import java.util.Optional; - -public interface RuleContext { - - RuleId getRuleId(); - - DeviceMetaData getDeviceMetaData(); - - Event save(Event event); - - Optional saveIfNotExists(Event event); - - Optional findEvent(String eventType, String eventUid); - - Optional findLatestAlarm(EntityId originator, String alarmType); - - Alarm createOrUpdateAlarm(Alarm alarm); - - ListenableFuture clearAlarm(AlarmId id, long clearTs); -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleFilter.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleFilter.java deleted file mode 100644 index 9489a539ca..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleFilter.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.rules; - -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.extensions.api.component.ConfigurableComponent; - -/** - * @author Andrew Shvayka - */ -public interface RuleFilter extends ConfigurableComponent, RuleLifecycleComponent { - - boolean filter(RuleContext ctx, ToDeviceActorMsg msg); - -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleInitializationException.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleInitializationException.java deleted file mode 100644 index c12f90f685..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleInitializationException.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.rules; - -public class RuleInitializationException extends RuleException { - - private static final long serialVersionUID = 1L; - - public RuleInitializationException(String msg, Exception e) { - super(msg, e); - } - - public RuleInitializationException(String msg) { - super(msg, null); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleProcessingMetaData.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleProcessingMetaData.java deleted file mode 100644 index 03cf9c80d3..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/RuleProcessingMetaData.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.rules; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -public class RuleProcessingMetaData { - - private final Map md; - - public RuleProcessingMetaData() { - super(); - this.md = new HashMap<>(); - } - - public void put(String key, T value) { - md.put(key, value); - } - - public Optional get(String key) { - return Optional.ofNullable((T) md.get(key)); - } - - public Map getValues() { - return Collections.unmodifiableMap(md); - } -} diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/ToRuleActorMsg.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/ToRuleActorMsg.java deleted file mode 100644 index 5dd386c31d..0000000000 --- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/rules/ToRuleActorMsg.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.api.rules; - -import org.thingsboard.server.common.data.id.RuleId; -import org.thingsboard.server.common.msg.aware.TenantAwareMsg; - -public interface ToRuleActorMsg extends TenantAwareMsg { - - RuleId getRuleId(); - -} diff --git a/extensions-api/src/main/resources/EmptyJsonDescriptor.json b/extensions-api/src/main/resources/EmptyJsonDescriptor.json deleted file mode 100644 index 03bf1454b8..0000000000 --- a/extensions-api/src/main/resources/EmptyJsonDescriptor.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "schema": { - "description": "Empty Schema", - "type": "object", - "additionalProperties": false, - "properties": { - } - }, - "form": [ - "*" - ] -} \ No newline at end of file diff --git a/extensions-core/pom.xml b/extensions-core/pom.xml deleted file mode 100644 index da8ff0faad..0000000000 --- a/extensions-core/pom.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - 4.0.0 - - org.thingsboard - 1.4.1-SNAPSHOT - thingsboard - - org.thingsboard - extensions-core - jar - - Thingsboard Server Core Extensions - https://thingsboard.io - - - UTF-8 - ${basedir}/.. - - - - - org.thingsboard - extensions-api - provided - - - org.thingsboard.common - transport - provided - - - com.google.code.gson - gson - - - org.apache.velocity - velocity - provided - - - org.apache.velocity - velocity-tools - provided - - - org.springframework.boot - spring-boot-starter-web - provided - - - org.springframework - spring-context-support - provided - - - javax.mail - mail - - - com.google.protobuf - protobuf-java - provided - - - com.google.guava - guava - provided - - - org.slf4j - slf4j-api - provided - - - org.slf4j - log4j-over-slf4j - provided - - - ch.qos.logback - logback-core - provided - - - ch.qos.logback - logback-classic - provided - - - junit - junit - test - - - org.mockito - mockito-all - test - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - org.xolstice.maven.plugins - protobuf-maven-plugin - - - org.codehaus.mojo - build-helper-maven-plugin - - - - - diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailAction.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailAction.java deleted file mode 100644 index f765a121e1..0000000000 --- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/mail/SendMailAction.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright © 2016-2018 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.extensions.core.action.mail; - -import lombok.extern.slf4j.Slf4j; -import org.apache.velocity.Template; -import org.apache.velocity.VelocityContext; -import org.apache.velocity.runtime.parser.ParseException; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; -import org.thingsboard.server.common.msg.session.ToDeviceMsg; -import org.thingsboard.server.extensions.api.component.Action; -import org.thingsboard.server.extensions.api.plugins.PluginAction; -import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; -import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; -import org.thingsboard.server.extensions.api.rules.RuleContext; -import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData; -import org.thingsboard.server.extensions.api.rules.SimpleRuleLifecycleComponent; -import org.thingsboard.server.extensions.core.utils.VelocityUtils; - -import java.util.Optional; - -/** - * @author Andrew Shvayka - */ -@Action(name = "Send Mail Action", descriptor = "SendMailActionDescriptor.json", configuration = SendMailActionConfiguration.class) -@Slf4j -public class SendMailAction extends SimpleRuleLifecycleComponent implements PluginAction { - - private SendMailActionConfiguration configuration; - private Optional