Commit 711ff2ab53af0e07d07535342eaca271db6b2bcc

Authored by Yura
2 parents 3f7f257d aa6d3fba

Merge remote-tracking branch 'origin/develop/1.5' into develop/cluter-refactoring

Showing 70 changed files with 2452 additions and 851 deletions

Too many changes to show.

To preserve performance only 70 of 269 files are displayed.

@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 # limitations under the License. 14 # limitations under the License.
15 # 15 #
16 16
17 -export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@" 17 +export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@"
18 export LOG_FILENAME=${pkg.name}.out 18 export LOG_FILENAME=${pkg.name}.out
19 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions 19 export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions
20 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql 20 export SQL_DATA_FOLDER=${pkg.installFolder}/data/sql
1 -{  
2 - "apiToken": "messaging",  
3 - "name": "Demo Device Messaging RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.messaging.DeviceMessagingPlugin",  
5 - "publicAccess": false,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "maxDeviceCountPerCustomer": 1024,  
9 - "defaultTimeout": 20000,  
10 - "maxTimeout": 60000  
11 - },  
12 - "additionalInfo": null  
13 -}  
1 -{  
2 - "apiToken": "mail",  
3 - "name": "Demo Email Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.mail.MailPlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "host": "smtp.sendgrid.net",  
9 - "port": 2525,  
10 - "username": "apikey",  
11 - "password": "your_api_key",  
12 - "otherProperties": [  
13 - {  
14 - "key": "mail.smtp.auth",  
15 - "value": "true"  
16 - },  
17 - {  
18 - "key": "mail.smtp.timeout",  
19 - "value": "10000"  
20 - },  
21 - {  
22 - "key": "mail.smtp.starttls.enable",  
23 - "value": "true"  
24 - }  
25 - ]  
26 - },  
27 - "additionalInfo": null  
28 -}  
1 -{  
2 - "apiToken": "time",  
3 - "name": "Demo Time RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.time.TimePlugin",  
5 - "publicAccess": false,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "timeFormat": "yyyy MM dd HH:mm:ss.SSS"  
9 - },  
10 - "additionalInfo": null  
11 -}  
1 -{  
2 - "name": "Demo Alarm Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "mail",  
6 - "filters": [  
7 - {  
8 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",  
9 - "name": "MsgTypeFilter",  
10 - "configuration": {  
11 - "messageTypes": [  
12 - "POST_TELEMETRY",  
13 - "POST_ATTRIBUTES",  
14 - "GET_ATTRIBUTES"  
15 - ]  
16 - }  
17 - },  
18 - {  
19 - "clazz": "org.thingsboard.server.extensions.core.filter.DeviceTelemetryFilter",  
20 - "name": "Temperature filter",  
21 - "configuration": {  
22 - "filter": "typeof temperature !== 'undefined' && temperature >= 100"  
23 - }  
24 - }  
25 - ],  
26 - "processor": {  
27 - "clazz": "org.thingsboard.server.extensions.core.processor.AlarmDeduplicationProcessor",  
28 - "name": "AlarmDeduplicationProcessor",  
29 - "configuration": {  
30 - "alarmIdTemplate": "[$date.get('yyyy-MM-dd HH:mm')] Device $cs.get('serialNumber')($cs.get('model')) temperature is high!",  
31 - "alarmBodyTemplate": "[$date.get('yyyy-MM-dd HH:mm:ss')] Device $cs.get('serialNumber')($cs.get('model')) temperature is $temp.valueAsString!"  
32 - }  
33 - },  
34 - "action": {  
35 - "clazz": "org.thingsboard.server.extensions.core.action.mail.SendMailAction",  
36 - "name": "Send Mail Action",  
37 - "configuration": {  
38 - "sendFlag": "isNewAlarm",  
39 - "fromTemplate": "thingsboard@gmail.com",  
40 - "toTemplate": "thingsboard@gmail.com",  
41 - "subjectTemplate": "$alarmId",  
42 - "bodyTemplate": "$alarmBody"  
43 - }  
44 - },  
45 - "additionalInfo": null  
46 -}  
1 -{  
2 - "name": "Demo getTime RPC Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "time",  
6 - "filters": [  
7 - {  
8 - "configuration": {  
9 - "messageTypes": [  
10 - "RPC_REQUEST"  
11 - ]  
12 - },  
13 - "name": "RPC Request Filter",  
14 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"  
15 - },  
16 - {  
17 - "configuration": {  
18 - "methodNames": [  
19 - {  
20 - "name": "getTime"  
21 - }  
22 - ]  
23 - },  
24 - "name": "getTime method filter",  
25 - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"  
26 - }  
27 - ],  
28 - "processor": null,  
29 - "action": {  
30 - "configuration": {},  
31 - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",  
32 - "name": "getTimeAction"  
33 - },  
34 - "additionalInfo": null  
35 -}  
1 -{  
2 - "name": "Demo Messaging RPC Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "messaging",  
6 - "filters": [  
7 - {  
8 - "configuration": {  
9 - "messageTypes": [  
10 - "RPC_REQUEST"  
11 - ]  
12 - },  
13 - "name": "RPC Request Filter",  
14 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter"  
15 - },  
16 - {  
17 - "configuration": {  
18 - "methodNames": [  
19 - {  
20 - "name": "getDevices"  
21 - },  
22 - {  
23 - "name": "sendMsg"  
24 - }  
25 - ]  
26 - },  
27 - "name": "Messaging methods filter",  
28 - "clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter"  
29 - }  
30 - ],  
31 - "processor": null,  
32 - "action": {  
33 - "configuration": {},  
34 - "clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction",  
35 - "name": "Messaging RPC Action"  
36 - },  
37 - "additionalInfo": null  
38 -}  
1 -{  
2 - "apiToken": "rpc",  
3 - "name": "System RPC Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {  
8 - "defaultTimeout": 20000  
9 - },  
10 - "additionalInfo": null  
11 -}  
1 -{  
2 - "apiToken": "telemetry",  
3 - "name": "System Telemetry Plugin",  
4 - "clazz": "org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin",  
5 - "publicAccess": true,  
6 - "state": "ACTIVE",  
7 - "configuration": {},  
8 - "additionalInfo": null  
9 -}  
1 -{  
2 - "name": "System Telemetry Rule",  
3 - "state": "ACTIVE",  
4 - "weight": 0,  
5 - "pluginToken": "telemetry",  
6 - "filters": [  
7 - {  
8 - "clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter",  
9 - "name": "TelemetryFilter",  
10 - "configuration": {  
11 - "messageTypes": [  
12 - "POST_TELEMETRY",  
13 - "POST_ATTRIBUTES",  
14 - "GET_ATTRIBUTES"  
15 - ]  
16 - }  
17 - }  
18 - ],  
19 - "processor": null,  
20 - "action": {  
21 - "clazz": "org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction",  
22 - "name": "TelemetryMsgConverterAction",  
23 - "configuration": {  
24 - "timeUnit": "DAYS",  
25 - "ttlValue": 365  
26 - }  
27 - },  
28 - "additionalInfo": null  
29 -}  
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Root Rule Chain",
  5 + "firstRuleNodeId": null,
  6 + "root": true,
  7 + "debugMode": false,
  8 + "configuration": null
  9 + },
  10 + "metadata": {
  11 + "firstNodeIndex": 2,
  12 + "nodes": [
  13 + {
  14 + "additionalInfo": {
  15 + "layoutX": 824,
  16 + "layoutY": 156
  17 + },
  18 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  19 + "name": "SaveTS",
  20 + "debugMode": true,
  21 + "configuration": {
  22 + "defaultTTL": 0
  23 + }
  24 + },
  25 + {
  26 + "additionalInfo": {
  27 + "layoutX": 825,
  28 + "layoutY": 52
  29 + },
  30 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  31 + "name": "save client attributes",
  32 + "debugMode": true,
  33 + "configuration": {
  34 + "scope": "CLIENT_SCOPE"
  35 + }
  36 + },
  37 + {
  38 + "additionalInfo": {
  39 + "layoutX": 347,
  40 + "layoutY": 149
  41 + },
  42 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  43 + "name": "Message Type Switch",
  44 + "debugMode": false,
  45 + "configuration": {
  46 + "version": 0
  47 + }
  48 + },
  49 + {
  50 + "additionalInfo": {
  51 + "layoutX": 825,
  52 + "layoutY": 266
  53 + },
  54 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  55 + "name": "Log RPC",
  56 + "debugMode": false,
  57 + "configuration": {
  58 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  59 + }
  60 + },
  61 + {
  62 + "additionalInfo": {
  63 + "layoutX": 825,
  64 + "layoutY": 379
  65 + },
  66 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  67 + "name": "Log Other",
  68 + "debugMode": false,
  69 + "configuration": {
  70 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  71 + }
  72 + }
  73 + ],
  74 + "connections": [
  75 + {
  76 + "fromIndex": 2,
  77 + "toIndex": 4,
  78 + "type": "Other"
  79 + },
  80 + {
  81 + "fromIndex": 2,
  82 + "toIndex": 1,
  83 + "type": "Post attributes"
  84 + },
  85 + {
  86 + "fromIndex": 2,
  87 + "toIndex": 0,
  88 + "type": "Post telemetry"
  89 + },
  90 + {
  91 + "fromIndex": 2,
  92 + "toIndex": 3,
  93 + "type": "RPC Request"
  94 + }
  95 + ],
  96 + "ruleChainConnections": null
  97 + }
  98 +}
@@ -84,6 +84,7 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_sear @@ -84,6 +84,7 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_sear
84 84
85 CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( 85 CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
86 id uuid, 86 id uuid,
  87 + rule_chain_id uuid,
87 type text, 88 type text,
88 name text, 89 name text,
89 debug_mode boolean, 90 debug_mode boolean,
@@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS rule_chain ( @@ -28,6 +28,7 @@ CREATE TABLE IF NOT EXISTS rule_chain (
28 28
29 CREATE TABLE IF NOT EXISTS rule_node ( 29 CREATE TABLE IF NOT EXISTS rule_node (
30 id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, 30 id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
  31 + rule_chain_id varchar(31),
31 additional_info varchar, 32 additional_info varchar,
32 configuration varchar(10000000), 33 configuration varchar(10000000),
33 type varchar(255), 34 type varchar(255),
@@ -49,6 +49,7 @@ import org.thingsboard.server.dao.customer.CustomerService; @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
49 import org.thingsboard.server.dao.device.DeviceService; 49 import org.thingsboard.server.dao.device.DeviceService;
50 import org.thingsboard.server.dao.event.EventService; 50 import org.thingsboard.server.dao.event.EventService;
51 import org.thingsboard.server.dao.plugin.PluginService; 51 import org.thingsboard.server.dao.plugin.PluginService;
  52 +import org.thingsboard.server.dao.queue.MsgQueue;
52 import org.thingsboard.server.dao.relation.RelationService; 53 import org.thingsboard.server.dao.relation.RelationService;
53 import org.thingsboard.server.dao.rule.RuleChainService; 54 import org.thingsboard.server.dao.rule.RuleChainService;
54 import org.thingsboard.server.dao.rule.RuleService; 55 import org.thingsboard.server.dao.rule.RuleService;
@@ -60,8 +61,11 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; @@ -60,8 +61,11 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
60 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; 61 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
61 import org.thingsboard.server.service.component.ComponentDiscoveryService; 62 import org.thingsboard.server.service.component.ComponentDiscoveryService;
62 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 63 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  64 +import org.thingsboard.server.service.executors.ExternalCallExecutorService;
63 import org.thingsboard.server.service.mail.MailExecutorService; 65 import org.thingsboard.server.service.mail.MailExecutorService;
  66 +import org.thingsboard.server.service.rpc.DeviceRpcService;
64 import org.thingsboard.server.service.script.JsExecutorService; 67 import org.thingsboard.server.service.script.JsExecutorService;
  68 +import org.thingsboard.server.service.state.DeviceStateService;
65 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; 69 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
66 70
67 import java.io.IOException; 71 import java.io.IOException;
@@ -163,6 +167,10 @@ public class ActorSystemContext { @@ -163,6 +167,10 @@ public class ActorSystemContext {
163 167
164 @Autowired 168 @Autowired
165 @Getter 169 @Getter
  170 + private DeviceRpcService deviceRpcService;
  171 +
  172 + @Autowired
  173 + @Getter
166 @Setter 174 @Setter
167 private PluginWebSocketMsgEndpoint wsMsgEndpoint; 175 private PluginWebSocketMsgEndpoint wsMsgEndpoint;
168 176
@@ -180,23 +188,35 @@ public class ActorSystemContext { @@ -180,23 +188,35 @@ public class ActorSystemContext {
180 188
181 @Autowired 189 @Autowired
182 @Getter 190 @Getter
  191 + private ExternalCallExecutorService externalCallExecutorService;
  192 +
  193 + @Autowired
  194 + @Getter
183 private MailService mailService; 195 private MailService mailService;
184 196
  197 + @Autowired
  198 + @Getter
  199 + private MsgQueue msgQueue;
  200 +
  201 + @Autowired
  202 + @Getter
  203 + private DeviceStateService deviceStateService;
  204 +
185 @Value("${actors.session.sync.timeout}") 205 @Value("${actors.session.sync.timeout}")
186 @Getter 206 @Getter
187 private long syncSessionTimeout; 207 private long syncSessionTimeout;
188 208
189 - @Value("${actors.plugin.termination.delay}") 209 + @Value("${actors.queue.enabled}")
190 @Getter 210 @Getter
191 - private long pluginActorTerminationDelay; 211 + private boolean queuePersistenceEnabled;
192 212
193 - @Value("${actors.plugin.processing.timeout}") 213 + @Value("${actors.queue.timeout}")
194 @Getter 214 @Getter
195 - private long pluginProcessingTimeout; 215 + private long queuePersistenceTimeout;
196 216
197 - @Value("${actors.plugin.error_persist_frequency}") 217 + @Value("${actors.client_side_rpc.timeout}")
198 @Getter 218 @Getter
199 - private long pluginErrorPersistFrequency; 219 + private long clientSideRpcTimeout;
200 220
201 @Value("${actors.rule.chain.error_persist_frequency}") 221 @Value("${actors.rule.chain.error_persist_frequency}")
202 @Getter 222 @Getter
@@ -206,14 +226,6 @@ public class ActorSystemContext { @@ -206,14 +226,6 @@ public class ActorSystemContext {
206 @Getter 226 @Getter
207 private long ruleNodeErrorPersistFrequency; 227 private long ruleNodeErrorPersistFrequency;
208 228
209 - @Value("${actors.rule.termination.delay}")  
210 - @Getter  
211 - private long ruleActorTerminationDelay;  
212 -  
213 - @Value("${actors.rule.error_persist_frequency}")  
214 - @Getter  
215 - private long ruleErrorPersistFrequency;  
216 -  
217 @Value("${actors.statistics.enabled}") 229 @Value("${actors.statistics.enabled}")
218 @Getter 230 @Getter
219 private boolean statisticsEnabled; 231 private boolean statisticsEnabled;
@@ -29,13 +29,12 @@ import org.thingsboard.server.actors.shared.plugin.SystemPluginManager; @@ -29,13 +29,12 @@ import org.thingsboard.server.actors.shared.plugin.SystemPluginManager;
29 import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager; 29 import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
30 import org.thingsboard.server.actors.tenant.TenantActor; 30 import org.thingsboard.server.actors.tenant.TenantActor;
31 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
32 -import org.thingsboard.server.common.data.id.PluginId;  
33 -import org.thingsboard.server.common.data.id.RuleChainId;  
34 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.page.PageDataIterable; 33 import org.thingsboard.server.common.data.page.PageDataIterable;
36 import org.thingsboard.server.common.msg.TbActorMsg; 34 import org.thingsboard.server.common.msg.TbActorMsg;
37 -import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;  
38 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 35 +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
  36 +import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  37 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
40 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 39 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
41 import org.thingsboard.server.dao.model.ModelConstants; 40 import org.thingsboard.server.dao.model.ModelConstants;
@@ -90,12 +89,23 @@ public class AppActor extends RuleChainManagerActor { @@ -90,12 +89,23 @@ public class AppActor extends RuleChainManagerActor {
90 @Override 89 @Override
91 protected boolean process(TbActorMsg msg) { 90 protected boolean process(TbActorMsg msg) {
92 switch (msg.getMsgType()) { 91 switch (msg.getMsgType()) {
  92 + case CLUSTER_EVENT_MSG:
  93 + broadcast(msg);
  94 + break;
93 case COMPONENT_LIFE_CYCLE_MSG: 95 case COMPONENT_LIFE_CYCLE_MSG:
94 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 96 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
95 break; 97 break;
96 case SERVICE_TO_RULE_ENGINE_MSG: 98 case SERVICE_TO_RULE_ENGINE_MSG:
97 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); 99 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
98 break; 100 break;
  101 + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
  102 + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
  103 + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
  104 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  105 + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  106 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  107 + onToDeviceActorMsg((TenantAwareMsg) msg);
  108 + break;
99 default: 109 default:
100 return false; 110 return false;
101 } 111 }
@@ -110,48 +120,12 @@ public class AppActor extends RuleChainManagerActor { @@ -110,48 +120,12 @@ public class AppActor extends RuleChainManagerActor {
110 } 120 }
111 } 121 }
112 122
113 -  
114 -// @Override  
115 -// public void onReceive(Object msg) throws Exception {  
116 -// logger.debug("Received message: {}", msg);  
117 -// if (msg instanceof ToDeviceActorMsg) {  
118 -// processDeviceMsg((ToDeviceActorMsg) msg);  
119 -// } else if (msg instanceof ToPluginActorMsg) {  
120 -// onToPluginMsg((ToPluginActorMsg) msg);  
121 -// } else if (msg instanceof ToDeviceActorNotificationMsg) {  
122 -// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);  
123 -// } else if (msg instanceof Terminated) {  
124 -// processTermination((Terminated) msg);  
125 -// } else if (msg instanceof ClusterEventMsg) {  
126 -// broadcast(msg);  
127 -// } else if (msg instanceof ComponentLifecycleMsg) {  
128 -// onComponentLifecycleMsg((ComponentLifecycleMsg) msg);  
129 -// } else if (msg instanceof PluginTerminationMsg) {  
130 -// onPluginTerminated((PluginTerminationMsg) msg);  
131 -// } else {  
132 -// logger.warning("Unknown message: {}!", msg);  
133 -// }  
134 -// }  
135 -  
136 - private void onPluginTerminated(PluginTerminationMsg msg) {  
137 - pluginManager.remove(msg.getId());  
138 - }  
139 -  
140 - private void broadcast(Object msg) {  
141 - pluginManager.broadcast(msg); 123 + @Override
  124 + protected void broadcast(Object msg) {
  125 + super.broadcast(msg);
142 tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); 126 tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
143 } 127 }
144 128
145 - private void onToPluginMsg(ToPluginActorMsg msg) {  
146 - ActorRef target;  
147 - if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) {  
148 - target = pluginManager.getOrCreateActor(this.context(), msg.getPluginId());  
149 - } else {  
150 - target = getOrCreateTenantActor(msg.getPluginTenantId());  
151 - }  
152 - target.tell(msg, ActorRef.noSender());  
153 - }  
154 -  
155 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 129 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
156 ActorRef target; 130 ActorRef target;
157 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 131 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
@@ -166,17 +140,17 @@ public class AppActor extends RuleChainManagerActor { @@ -166,17 +140,17 @@ public class AppActor extends RuleChainManagerActor {
166 } 140 }
167 } 141 }
168 142
169 - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) { 143 + private void onToDeviceActorMsg(TenantAwareMsg msg) {
170 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender()); 144 getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
171 } 145 }
172 146
173 - private void processDeviceMsg(ToDeviceActorMsg toDeviceActorMsg) {  
174 - TenantId tenantId = toDeviceActorMsg.getTenantId(); 147 + private void processDeviceMsg(DeviceToDeviceActorMsg deviceToDeviceActorMsg) {
  148 + TenantId tenantId = deviceToDeviceActorMsg.getTenantId();
175 ActorRef tenantActor = getOrCreateTenantActor(tenantId); 149 ActorRef tenantActor = getOrCreateTenantActor(tenantId);
176 - if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {  
177 -// tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self()); 150 + if (deviceToDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
  151 +// tenantActor.tell(new RuleChainDeviceMsg(deviceToDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
178 } else { 152 } else {
179 - tenantActor.tell(toDeviceActorMsg, context().self()); 153 + tenantActor.tell(deviceToDeviceActorMsg, context().self());
180 } 154 }
181 } 155 }
182 156
@@ -24,59 +24,66 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -24,59 +24,66 @@ import org.thingsboard.server.common.data.id.DeviceId;
24 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.msg.TbActorMsg; 25 import org.thingsboard.server.common.msg.TbActorMsg;
26 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 26 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
27 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 27 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
  28 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
  29 +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
  30 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
28 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; 31 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
29 -import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;  
30 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; 32 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
31 -import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;  
32 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
33 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; 33 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  34 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
34 35
35 public class DeviceActor extends ContextAwareActor { 36 public class DeviceActor extends ContextAwareActor {
36 37
37 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this); 38 private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
38 39
39 - private final TenantId tenantId;  
40 - private final DeviceId deviceId;  
41 private final DeviceActorMessageProcessor processor; 40 private final DeviceActorMessageProcessor processor;
42 41
43 private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { 42 private DeviceActor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
44 super(systemContext); 43 super(systemContext);
45 - this.tenantId = tenantId;  
46 - this.deviceId = deviceId;  
47 - this.processor = new DeviceActorMessageProcessor(systemContext, logger, deviceId); 44 + this.processor = new DeviceActorMessageProcessor(systemContext, logger, tenantId, deviceId);
48 } 45 }
49 46
50 @Override 47 @Override
51 protected boolean process(TbActorMsg msg) { 48 protected boolean process(TbActorMsg msg) {
52 - return false;  
53 - }  
54 -  
55 - @Override  
56 - public void onReceive(Object msg) throws Exception {  
57 -// if (msg instanceof RuleChainDeviceMsg) {  
58 -// processor.process(context(), (RuleChainDeviceMsg) msg);  
59 -// } else if (msg instanceof RulesProcessedMsg) {  
60 -// processor.onRulesProcessedMsg(context(), (RulesProcessedMsg) msg);  
61 - if (msg instanceof ToDeviceActorMsg) {  
62 - processor.process(context(), (ToDeviceActorMsg) msg);  
63 - } else if (msg instanceof ToDeviceActorNotificationMsg) {  
64 - if (msg instanceof DeviceAttributesEventNotificationMsg) { 49 + switch (msg.getMsgType()) {
  50 + case CLUSTER_EVENT_MSG:
  51 + processor.processClusterEventMsg((ClusterEventMsg) msg);
  52 + break;
  53 + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
  54 + processor.process(context(), (DeviceToDeviceActorMsg) msg);
  55 + break;
  56 + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
65 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg); 57 processor.processAttributesUpdate(context(), (DeviceAttributesEventNotificationMsg) msg);
66 - } else if (msg instanceof ToDeviceRpcRequestPluginMsg) {  
67 - processor.processRpcRequest(context(), (ToDeviceRpcRequestPluginMsg) msg);  
68 - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg){ 58 + break;
  59 + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
69 processor.processCredentialsUpdate(); 60 processor.processCredentialsUpdate();
70 - } else if (msg instanceof DeviceNameOrTypeUpdateMsg){ 61 + break;
  62 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
71 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg); 63 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
72 - }  
73 - } else if (msg instanceof TimeoutMsg) {  
74 - processor.processTimeout(context(), (TimeoutMsg) msg);  
75 - } else if (msg instanceof ClusterEventMsg) {  
76 - processor.processClusterEventMsg((ClusterEventMsg) msg);  
77 - } else {  
78 - logger.debug("[{}][{}] Unknown msg type.", tenantId, deviceId, msg.getClass().getName()); 64 + break;
  65 + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  66 + processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
  67 + break;
  68 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  69 + processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg);
  70 + break;
  71 + case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
  72 + processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
  73 + break;
  74 + case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
  75 + processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
  76 + break;
  77 + case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
  78 + processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
  79 + break;
  80 + case RULE_ENGINE_QUEUE_PUT_ACK_MSG:
  81 + processor.processQueueAck(context(), (RuleEngineQueuePutAckMsg) msg);
  82 + break;
  83 + default:
  84 + return false;
79 } 85 }
  86 + return true;
80 } 87 }
81 88
82 public static class ActorCreator extends ContextBasedCreator<DeviceActor> { 89 public static class ActorCreator extends ContextBasedCreator<DeviceActor> {
@@ -18,30 +18,76 @@ package org.thingsboard.server.actors.device; @@ -18,30 +18,76 @@ package org.thingsboard.server.actors.device;
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
19 import akka.actor.ActorRef; 19 import akka.actor.ActorRef;
20 import akka.event.LoggingAdapter; 20 import akka.event.LoggingAdapter;
  21 +import com.datastax.driver.core.utils.UUIDs;
  22 +import com.google.common.util.concurrent.FutureCallback;
  23 +import com.google.common.util.concurrent.Futures;
  24 +import com.google.common.util.concurrent.ListenableFuture;
  25 +import com.google.gson.Gson;
  26 +import com.google.gson.JsonArray;
  27 +import com.google.gson.JsonObject;
  28 +import com.google.gson.JsonParser;
21 import org.thingsboard.server.actors.ActorSystemContext; 29 import org.thingsboard.server.actors.ActorSystemContext;
22 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
23 import org.thingsboard.server.common.data.DataConstants; 31 import org.thingsboard.server.common.data.DataConstants;
24 import org.thingsboard.server.common.data.Device; 32 import org.thingsboard.server.common.data.Device;
25 import org.thingsboard.server.common.data.id.DeviceId; 33 import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.SessionId; 34 import org.thingsboard.server.common.data.id.SessionId;
  35 +import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.kv.AttributeKey; 36 import org.thingsboard.server.common.data.kv.AttributeKey;
28 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 37 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  38 +import org.thingsboard.server.common.data.kv.KvEntry;
  39 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  40 +import org.thingsboard.server.common.msg.TbMsg;
  41 +import org.thingsboard.server.common.msg.TbMsgDataType;
  42 +import org.thingsboard.server.common.msg.TbMsgMetaData;
29 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 43 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
30 import org.thingsboard.server.common.msg.cluster.ServerAddress; 44 import org.thingsboard.server.common.msg.cluster.ServerAddress;
31 -import org.thingsboard.server.common.msg.core.*;  
32 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 45 +import org.thingsboard.server.common.msg.core.AttributesUpdateNotification;
  46 +import org.thingsboard.server.common.msg.core.AttributesUpdateRequest;
  47 +import org.thingsboard.server.common.msg.core.BasicCommandAckResponse;
  48 +import org.thingsboard.server.common.msg.core.BasicGetAttributesResponse;
  49 +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
  50 +import org.thingsboard.server.common.msg.core.BasicToDeviceSessionActorMsg;
  51 +import org.thingsboard.server.common.msg.core.GetAttributesRequest;
  52 +import org.thingsboard.server.common.msg.core.RuleEngineError;
  53 +import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
  54 +import org.thingsboard.server.common.msg.core.SessionCloseMsg;
  55 +import org.thingsboard.server.common.msg.core.SessionCloseNotification;
  56 +import org.thingsboard.server.common.msg.core.SessionOpenMsg;
  57 +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
  58 +import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
  59 +import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
  60 +import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
  61 +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
  62 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
33 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; 63 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
  64 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
34 import org.thingsboard.server.common.msg.session.FromDeviceMsg; 65 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
35 -import org.thingsboard.server.common.msg.session.MsgType; 66 +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
  67 +import org.thingsboard.server.common.msg.session.SessionMsgType;
36 import org.thingsboard.server.common.msg.session.SessionType; 68 import org.thingsboard.server.common.msg.session.SessionType;
37 import org.thingsboard.server.common.msg.session.ToDeviceMsg; 69 import org.thingsboard.server.common.msg.session.ToDeviceMsg;
38 -import org.thingsboard.server.extensions.api.device.DeviceAttributes; 70 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
  71 +import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
  72 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
39 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; 73 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
40 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; 74 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
41 -import org.thingsboard.server.extensions.api.plugins.msg.*;  
42 -  
43 -import java.util.*;  
44 -import java.util.concurrent.ExecutionException; 75 +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
  76 +import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
  77 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  78 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
  79 +
  80 +import javax.annotation.Nullable;
  81 +import java.util.ArrayList;
  82 +import java.util.Arrays;
  83 +import java.util.Collections;
  84 +import java.util.HashMap;
  85 +import java.util.HashSet;
  86 +import java.util.List;
  87 +import java.util.Map;
  88 +import java.util.Optional;
  89 +import java.util.Set;
  90 +import java.util.UUID;
45 import java.util.concurrent.TimeoutException; 91 import java.util.concurrent.TimeoutException;
46 import java.util.function.Consumer; 92 import java.util.function.Consumer;
47 import java.util.function.Predicate; 93 import java.util.function.Predicate;
@@ -52,46 +98,46 @@ import java.util.stream.Collectors; @@ -52,46 +98,46 @@ import java.util.stream.Collectors;
52 */ 98 */
53 public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { 99 public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
54 100
  101 + private final TenantId tenantId;
55 private final DeviceId deviceId; 102 private final DeviceId deviceId;
56 private final Map<SessionId, SessionInfo> sessions; 103 private final Map<SessionId, SessionInfo> sessions;
57 private final Map<SessionId, SessionInfo> attributeSubscriptions; 104 private final Map<SessionId, SessionInfo> attributeSubscriptions;
58 private final Map<SessionId, SessionInfo> rpcSubscriptions; 105 private final Map<SessionId, SessionInfo> rpcSubscriptions;
  106 + private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
  107 + private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
  108 + private final Map<UUID, PendingSessionMsgData> pendingMsgs;
59 109
60 - private final Map<Integer, ToDeviceRpcRequestMetadata> rpcPendingMap; 110 + private final Gson gson = new Gson();
  111 + private final JsonParser jsonParser = new JsonParser();
61 112
62 private int rpcSeq = 0; 113 private int rpcSeq = 0;
63 private String deviceName; 114 private String deviceName;
64 private String deviceType; 115 private String deviceType;
65 - private DeviceAttributes deviceAttributes; 116 + private TbMsgMetaData defaultMetaData;
66 117
67 - public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, DeviceId deviceId) { 118 + public DeviceActorMessageProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, DeviceId deviceId) {
68 super(systemContext, logger); 119 super(systemContext, logger);
  120 + this.tenantId = tenantId;
69 this.deviceId = deviceId; 121 this.deviceId = deviceId;
70 this.sessions = new HashMap<>(); 122 this.sessions = new HashMap<>();
71 this.attributeSubscriptions = new HashMap<>(); 123 this.attributeSubscriptions = new HashMap<>();
72 this.rpcSubscriptions = new HashMap<>(); 124 this.rpcSubscriptions = new HashMap<>();
73 - this.rpcPendingMap = new HashMap<>(); 125 + this.toDeviceRpcPendingMap = new HashMap<>();
  126 + this.toServerRpcPendingMap = new HashMap<>();
  127 + this.pendingMsgs = new HashMap<>();
74 initAttributes(); 128 initAttributes();
75 } 129 }
76 130
77 private void initAttributes() { 131 private void initAttributes() {
78 - //TODO: add invalidation of deviceType cache.  
79 Device device = systemContext.getDeviceService().findDeviceById(deviceId); 132 Device device = systemContext.getDeviceService().findDeviceById(deviceId);
80 this.deviceName = device.getName(); 133 this.deviceName = device.getName();
81 this.deviceType = device.getType(); 134 this.deviceType = device.getType();
82 - this.deviceAttributes = new DeviceAttributes(fetchAttributes(DataConstants.CLIENT_SCOPE),  
83 - fetchAttributes(DataConstants.SERVER_SCOPE), fetchAttributes(DataConstants.SHARED_SCOPE));  
84 - }  
85 -  
86 - private void refreshAttributes(DeviceAttributesEventNotificationMsg msg) {  
87 - if (msg.isDeleted()) {  
88 - msg.getDeletedKeys().forEach(key -> deviceAttributes.remove(key));  
89 - } else {  
90 - deviceAttributes.update(msg.getScope(), msg.getValues());  
91 - } 135 + this.defaultMetaData = new TbMsgMetaData();
  136 + this.defaultMetaData.putValue("deviceName", deviceName);
  137 + this.defaultMetaData.putValue("deviceType", deviceType);
92 } 138 }
93 139
94 - void processRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg) { 140 + void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
95 ToDeviceRpcRequest request = msg.getMsg(); 141 ToDeviceRpcRequest request = msg.getMsg();
96 ToDeviceRpcRequestBody body = request.getBody(); 142 ToDeviceRpcRequestBody body = request.getBody();
97 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( 143 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
@@ -118,9 +164,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -118,9 +164,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
118 syncSessionSet.forEach(rpcSubscriptions::remove); 164 syncSessionSet.forEach(rpcSubscriptions::remove);
119 165
120 if (request.isOneway() && sent) { 166 if (request.isOneway() && sent) {
121 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(msg, (String) null);  
122 - context.parent().tell(responsePluginMsg, ActorRef.noSender());  
123 logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 167 logger.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
  168 + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
124 } else { 169 } else {
125 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); 170 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
126 } 171 }
@@ -132,24 +177,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -132,24 +177,46 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
132 177
133 } 178 }
134 179
135 - private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestPluginMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {  
136 - rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));  
137 - TimeoutIntMsg timeoutMsg = new TimeoutIntMsg(rpcRequest.getRequestId(), timeout); 180 + private void registerPendingRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
  181 + toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
  182 + DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
138 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); 183 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
139 } 184 }
140 185
141 - public void processTimeout(ActorContext context, TimeoutMsg msg) {  
142 - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId()); 186 + void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
  187 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
143 if (requestMd != null) { 188 if (requestMd != null) {
144 logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 189 logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
145 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION);  
146 - context.parent().tell(responsePluginMsg, ActorRef.noSender()); 190 + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
  191 + null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
  192 + }
  193 + }
  194 +
  195 + void processQueueTimeout(ActorContext context, DeviceActorQueueTimeoutMsg msg) {
  196 + PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
  197 + if (data != null) {
  198 + logger.debug("[{}] Queue put [{}] timeout detected!", deviceId, msg.getId());
  199 + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(data.getSessionMsgType(), RuleEngineError.QUEUE_PUT_TIMEOUT);
  200 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
  201 + }
  202 + }
  203 +
  204 + void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
  205 + PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
  206 + if (data != null && data.isReplyOnQueueAck()) {
  207 + int remainingAcks = data.getAckMsgCount() - 1;
  208 + data.setAckMsgCount(remainingAcks);
  209 + logger.debug("[{}] Queue put [{}] ack detected. Remaining acks: {}!", deviceId, msg.getId(), remainingAcks);
  210 + if (remainingAcks == 0) {
  211 + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
  212 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
  213 + }
147 } 214 }
148 } 215 }
149 216
150 private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) { 217 private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) {
151 - if (!rpcPendingMap.isEmpty()) {  
152 - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId); 218 + if (!toDeviceRpcPendingMap.isEmpty()) {
  219 + logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
153 if (type == SessionType.SYNC) { 220 if (type == SessionType.SYNC) {
154 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); 221 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
155 rpcSubscriptions.remove(sessionId); 222 rpcSubscriptions.remove(sessionId);
@@ -159,12 +226,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -159,12 +226,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
159 } 226 }
160 Set<Integer> sentOneWayIds = new HashSet<>(); 227 Set<Integer> sentOneWayIds = new HashSet<>();
161 if (type == SessionType.ASYNC) { 228 if (type == SessionType.ASYNC) {
162 - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds)); 229 + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
163 } else { 230 } else {
164 - rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds)); 231 + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
165 } 232 }
166 233
167 - sentOneWayIds.forEach(rpcPendingMap::remove); 234 + sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
168 } 235 }
169 236
170 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) { 237 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
@@ -173,8 +240,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -173,8 +240,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
173 ToDeviceRpcRequestBody body = request.getBody(); 240 ToDeviceRpcRequestBody body = request.getBody();
174 if (request.isOneway()) { 241 if (request.isOneway()) {
175 sentOneWayIds.add(entry.getKey()); 242 sentOneWayIds.add(entry.getKey());
176 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null);  
177 - context.parent().tell(responsePluginMsg, ActorRef.noSender()); 243 + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(request.getId(), null, null));
178 } 244 }
179 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg( 245 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
180 entry.getKey(), 246 entry.getKey(),
@@ -186,14 +252,170 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -186,14 +252,170 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
186 }; 252 };
187 } 253 }
188 254
189 - void process(ActorContext context, ToDeviceActorMsg msg) { 255 + void process(ActorContext context, DeviceToDeviceActorMsg msg) {
190 processSubscriptionCommands(context, msg); 256 processSubscriptionCommands(context, msg);
191 processRpcResponses(context, msg); 257 processRpcResponses(context, msg);
192 processSessionStateMsgs(msg); 258 processSessionStateMsgs(msg);
  259 +
  260 + SessionMsgType sessionMsgType = msg.getPayload().getMsgType();
  261 + if (sessionMsgType.requiresRulesProcessing()) {
  262 + switch (sessionMsgType) {
  263 + case GET_ATTRIBUTES_REQUEST:
  264 + handleGetAttributesRequest(msg);
  265 + break;
  266 + case POST_ATTRIBUTES_REQUEST:
  267 + handlePostAttributesRequest(context, msg);
  268 + reportActivity();
  269 + break;
  270 + case POST_TELEMETRY_REQUEST:
  271 + handlePostTelemetryRequest(context, msg);
  272 + reportActivity();
  273 + break;
  274 + case TO_SERVER_RPC_REQUEST:
  275 + handleClientSideRPCRequest(context, msg);
  276 + reportActivity();
  277 + break;
  278 + }
  279 + }
  280 + }
  281 +
  282 + private void reportActivity() {
  283 + systemContext.getDeviceStateService().onDeviceActivity(deviceId);
  284 + }
  285 +
  286 + private void reportSessionOpen() {
  287 + systemContext.getDeviceStateService().onDeviceConnect(deviceId);
  288 + }
  289 +
  290 + private void reportSessionClose() {
  291 + systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
  292 + }
  293 +
  294 + private void handleGetAttributesRequest(DeviceToDeviceActorMsg src) {
  295 + GetAttributesRequest request = (GetAttributesRequest) src.getPayload();
  296 + ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, request.getClientAttributeNames());
  297 + ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, request.getClientAttributeNames());
  298 +
  299 + Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
  300 + @Override
  301 + public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
  302 + BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(),
  303 + request.getRequestId(), BasicAttributeKVMsg.from(result.get(0), result.get(1)));
  304 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, src.getSessionId()), src.getServerAddress());
  305 + }
  306 +
  307 + @Override
  308 + public void onFailure(Throwable t) {
  309 + if (t instanceof Exception) {
  310 + ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onError(SessionMsgType.GET_ATTRIBUTES_REQUEST, request.getRequestId(), (Exception) t);
  311 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, src.getSessionId()), src.getServerAddress());
  312 + } else {
  313 + logger.error("[{}] Failed to process attributes request", deviceId, t);
  314 + }
  315 + }
  316 + });
  317 + }
  318 +
  319 + private ListenableFuture<List<AttributeKvEntry>> getAttributeKvEntries(DeviceId deviceId, String scope, Optional<Set<String>> names) {
  320 + if (names.isPresent()) {
  321 + if (!names.get().isEmpty()) {
  322 + return systemContext.getAttributesService().find(deviceId, scope, names.get());
  323 + } else {
  324 + return systemContext.getAttributesService().findAll(deviceId, scope);
  325 + }
  326 + } else {
  327 + return Futures.immediateFuture(Collections.emptyList());
  328 + }
  329 + }
  330 +
  331 + private void handlePostAttributesRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  332 + AttributesUpdateRequest request = (AttributesUpdateRequest) src.getPayload();
  333 +
  334 + JsonObject json = new JsonObject();
  335 + for (AttributeKvEntry kv : request.getAttributes()) {
  336 + kv.getBooleanValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  337 + kv.getLongValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  338 + kv.getDoubleValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  339 + kv.getStrValue().ifPresent(v -> json.addProperty(kv.getKey(), v));
  340 + }
  341 +
  342 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  343 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
  344 + SessionMsgType.POST_ATTRIBUTES_REQUEST, request.getRequestId(), true, 1);
  345 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  346 + }
  347 +
  348 + private void handlePostTelemetryRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  349 + TelemetryUploadRequest request = (TelemetryUploadRequest) src.getPayload();
  350 +
  351 + Map<Long, List<KvEntry>> tsData = request.getData();
  352 +
  353 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(),
  354 + SessionMsgType.POST_TELEMETRY_REQUEST, request.getRequestId(), true, tsData.size());
  355 +
  356 + for (Map.Entry<Long, List<KvEntry>> entry : tsData.entrySet()) {
  357 + JsonObject json = new JsonObject();
  358 + json.addProperty("ts", entry.getKey());
  359 + JsonObject values = new JsonObject();
  360 + for (KvEntry kv : entry.getValue()) {
  361 + kv.getBooleanValue().ifPresent(v -> values.addProperty(kv.getKey(), v));
  362 + kv.getLongValue().ifPresent(v -> values.addProperty(kv.getKey(), v));
  363 + kv.getDoubleValue().ifPresent(v -> values.addProperty(kv.getKey(), v));
  364 + kv.getStrValue().ifPresent(v -> values.addProperty(kv.getKey(), v));
  365 + }
  366 + json.add("values", values);
  367 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, defaultMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  368 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  369 + }
  370 + }
  371 +
  372 + private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  373 + ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
  374 +
  375 + JsonObject json = new JsonObject();
  376 + json.addProperty("method", request.getMethod());
  377 + json.add("params", jsonParser.parse(request.getParams()));
  378 +
  379 + TbMsgMetaData requestMetaData = defaultMetaData.copy();
  380 + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
  381 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  382 + PendingSessionMsgData msgData = new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), SessionMsgType.TO_SERVER_RPC_REQUEST, request.getRequestId(), false, 1);
  383 + pushToRuleEngineWithTimeout(context, tbMsg, msgData);
  384 +
  385 + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
  386 + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
  387 + }
  388 +
  389 + public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
  390 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
  391 + if (data != null) {
  392 + logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
  393 + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
  394 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
  395 + }
  396 + }
  397 +
  398 + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
  399 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
  400 + if (data != null) {
  401 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
  402 + }
  403 + }
  404 +
  405 + private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, PendingSessionMsgData pendingMsgData) {
  406 + SessionMsgType sessionMsgType = pendingMsgData.getSessionMsgType();
  407 + int requestId = pendingMsgData.getRequestId();
  408 + if (systemContext.isQueuePersistenceEnabled()) {
  409 + pendingMsgs.put(tbMsg.getId(), pendingMsgData);
  410 + scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
  411 + } else {
  412 + ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), pendingMsgData.getSessionId());
  413 + sendMsgToSessionActor(response, pendingMsgData.getServerAddress());
  414 + }
  415 + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
193 } 416 }
194 417
195 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { 418 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
196 - refreshAttributes(msg);  
197 if (attributeSubscriptions.size() > 0) { 419 if (attributeSubscriptions.size() > 0) {
198 ToDeviceMsg notification = null; 420 ToDeviceMsg notification = null;
199 if (msg.isDeleted()) { 421 if (msg.isDeleted()) {
@@ -223,50 +445,29 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -223,50 +445,29 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
223 } 445 }
224 } 446 }
225 447
226 -// void process(ActorContext context, RuleChainDeviceMsg srcMsg) {  
227 -// ChainProcessingMetaData md = new ChainProcessingMetaData(srcMsg.getRuleChain(),  
228 -// srcMsg.getToDeviceActorMsg(), new DeviceMetaData(deviceId, deviceName, deviceType, deviceAttributes), context.self());  
229 -// ChainProcessingContext ctx = new ChainProcessingContext(md);  
230 -// if (ctx.getChainLength() > 0) {  
231 -// RuleProcessingMsg msg = new RuleProcessingMsg(ctx);  
232 -// ActorRef ruleActorRef = ctx.getCurrentActor();  
233 -// ruleActorRef.tell(msg, ActorRef.noSender());  
234 -// } else {  
235 -// context.self().tell(new RulesProcessedMsg(ctx), context.self());  
236 -// }  
237 -// }  
238 -  
239 - void processRpcResponses(ActorContext context, ToDeviceActorMsg msg) { 448 + private void processRpcResponses(ActorContext context, DeviceToDeviceActorMsg msg) {
240 SessionId sessionId = msg.getSessionId(); 449 SessionId sessionId = msg.getSessionId();
241 FromDeviceMsg inMsg = msg.getPayload(); 450 FromDeviceMsg inMsg = msg.getPayload();
242 - if (inMsg.getMsgType() == MsgType.TO_DEVICE_RPC_RESPONSE) { 451 + if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
243 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId); 452 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
244 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg; 453 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
245 - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId()); 454 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
246 boolean success = requestMd != null; 455 boolean success = requestMd != null;
247 if (success) { 456 if (success) {
248 - ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(requestMd.getMsg(), responseMsg.getData());  
249 - Optional<ServerAddress> pluginServerAddress = requestMd.getMsg().getServerAddress();  
250 - if (pluginServerAddress.isPresent()) {  
251 - systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg);  
252 - logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());  
253 - } else {  
254 - context.parent().tell(responsePluginMsg, ActorRef.noSender());  
255 - logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());  
256 - } 457 + systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), responseMsg.getData(), null));
257 } else { 458 } else {
258 logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 459 logger.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
259 } 460 }
260 if (msg.getSessionType() == SessionType.SYNC) { 461 if (msg.getSessionType() == SessionType.SYNC) {
261 BasicCommandAckResponse response = success 462 BasicCommandAckResponse response = success
262 - ? BasicCommandAckResponse.onSuccess(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())  
263 - : BasicCommandAckResponse.onError(MsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException()); 463 + ? BasicCommandAckResponse.onSuccess(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId())
  464 + : BasicCommandAckResponse.onError(SessionMsgType.TO_DEVICE_RPC_REQUEST, responseMsg.getRequestId(), new TimeoutException());
264 sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress()); 465 sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(response, msg.getSessionId()), msg.getServerAddress());
265 } 466 }
266 } 467 }
267 } 468 }
268 469
269 - public void processClusterEventMsg(ClusterEventMsg msg) { 470 + void processClusterEventMsg(ClusterEventMsg msg) {
270 if (!msg.isAdded()) { 471 if (!msg.isAdded()) {
271 logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress()); 472 logger.debug("[{}] Clearing attributes/rpc subscription for server [{}]", deviceId, msg.getServerAddress());
272 Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer() 473 Predicate<Map.Entry<SessionId, SessionInfo>> filter = e -> e.getValue().getServer()
@@ -276,69 +477,43 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -276,69 +477,43 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
276 } 477 }
277 } 478 }
278 479
279 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data) {  
280 - return toPluginRpcResponseMsg(requestMsg, data, null);  
281 - }  
282 -  
283 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, RpcError error) {  
284 - return toPluginRpcResponseMsg(requestMsg, null, error);  
285 - }  
286 -  
287 - private ToPluginRpcResponseDeviceMsg toPluginRpcResponseMsg(ToDeviceRpcRequestPluginMsg requestMsg, String data, RpcError error) {  
288 - return new ToPluginRpcResponseDeviceMsg(  
289 - requestMsg.getPluginId(),  
290 - requestMsg.getPluginTenantId(),  
291 - new FromDeviceRpcResponse(requestMsg.getMsg().getId(),  
292 - data,  
293 - error  
294 - )  
295 - );  
296 - }  
297 -  
298 -// void onRulesProcessedMsg(ActorContext context, RulesProcessedMsg msg) {  
299 -// ChainProcessingContext ctx = msg.getCtx();  
300 -// ToDeviceActorMsg inMsg = ctx.getInMsg();  
301 -// SessionId sid = inMsg.getSessionId();  
302 -// ToDeviceSessionActorMsg response;  
303 -// if (ctx.getResponse() != null) {  
304 -// response = new BasicToDeviceSessionActorMsg(ctx.getResponse(), sid);  
305 -// } else {  
306 -// response = new BasicToDeviceSessionActorMsg(ctx.getError(), sid);  
307 -// }  
308 -// sendMsgToSessionActor(response, inMsg.getServerAddress());  
309 -// }  
310 -  
311 - private void processSubscriptionCommands(ActorContext context, ToDeviceActorMsg msg) { 480 + private void processSubscriptionCommands(ActorContext context, DeviceToDeviceActorMsg msg) {
312 SessionId sessionId = msg.getSessionId(); 481 SessionId sessionId = msg.getSessionId();
313 SessionType sessionType = msg.getSessionType(); 482 SessionType sessionType = msg.getSessionType();
314 FromDeviceMsg inMsg = msg.getPayload(); 483 FromDeviceMsg inMsg = msg.getPayload();
315 - if (inMsg.getMsgType() == MsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) { 484 + if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST) {
316 logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); 485 logger.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
317 attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); 486 attributeSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
318 - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) { 487 + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST) {
319 logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId); 488 logger.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
320 attributeSubscriptions.remove(sessionId); 489 attributeSubscriptions.remove(sessionId);
321 - } else if (inMsg.getMsgType() == MsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) { 490 + } else if (inMsg.getMsgType() == SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST) {
322 logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); 491 logger.debug("[{}] Registering rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
323 rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress())); 492 rpcSubscriptions.put(sessionId, new SessionInfo(sessionType, msg.getServerAddress()));
324 sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress()); 493 sendPendingRequests(context, sessionId, sessionType, msg.getServerAddress());
325 - } else if (inMsg.getMsgType() == MsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) { 494 + } else if (inMsg.getMsgType() == SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST) {
326 logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType); 495 logger.debug("[{}] Canceling rpc subscription for session [{}][{}]", deviceId, sessionId, sessionType);
327 rpcSubscriptions.remove(sessionId); 496 rpcSubscriptions.remove(sessionId);
328 } 497 }
329 } 498 }
330 499
331 - private void processSessionStateMsgs(ToDeviceActorMsg msg) { 500 + private void processSessionStateMsgs(DeviceToDeviceActorMsg msg) {
332 SessionId sessionId = msg.getSessionId(); 501 SessionId sessionId = msg.getSessionId();
333 FromDeviceMsg inMsg = msg.getPayload(); 502 FromDeviceMsg inMsg = msg.getPayload();
334 if (inMsg instanceof SessionOpenMsg) { 503 if (inMsg instanceof SessionOpenMsg) {
335 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId); 504 logger.debug("[{}] Processing new session [{}]", deviceId, sessionId);
336 sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress())); 505 sessions.put(sessionId, new SessionInfo(SessionType.ASYNC, msg.getServerAddress()));
  506 + if (sessions.size() == 1) {
  507 + reportSessionOpen();
  508 + }
337 } else if (inMsg instanceof SessionCloseMsg) { 509 } else if (inMsg instanceof SessionCloseMsg) {
338 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); 510 logger.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
339 sessions.remove(sessionId); 511 sessions.remove(sessionId);
340 attributeSubscriptions.remove(sessionId); 512 attributeSubscriptions.remove(sessionId);
341 rpcSubscriptions.remove(sessionId); 513 rpcSubscriptions.remove(sessionId);
  514 + if (sessions.isEmpty()) {
  515 + reportSessionClose();
  516 + }
342 } 517 }
343 } 518 }
344 519
@@ -352,17 +527,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -352,17 +527,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
352 } 527 }
353 } 528 }
354 529
355 - private List<AttributeKvEntry> fetchAttributes(String scope) {  
356 - try {  
357 - //TODO: replace this with async operation. Happens only during actor creation, but is still criticla for performance,  
358 - return systemContext.getAttributesService().findAll(this.deviceId, scope).get();  
359 - } catch (InterruptedException | ExecutionException e) {  
360 - logger.warning("[{}] Failed to fetch attributes for scope: {}", deviceId, scope);  
361 - throw new RuntimeException(e);  
362 - }  
363 - }  
364 -  
365 - public void processCredentialsUpdate() { 530 + void processCredentialsUpdate() {
366 sessions.forEach((k, v) -> { 531 sessions.forEach((k, v) -> {
367 sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer()); 532 sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(new SessionCloseNotification(), k), v.getServer());
368 }); 533 });
@@ -370,8 +535,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso @@ -370,8 +535,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
370 rpcSubscriptions.clear(); 535 rpcSubscriptions.clear();
371 } 536 }
372 537
373 - public void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { 538 + void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
374 this.deviceName = msg.getDeviceName(); 539 this.deviceName = msg.getDeviceName();
375 this.deviceType = msg.getDeviceType(); 540 this.deviceType = msg.getDeviceType();
  541 + this.defaultMetaData = new TbMsgMetaData();
  542 + this.defaultMetaData.putValue("deviceName", deviceName);
  543 + this.defaultMetaData.putValue("deviceType", deviceType);
376 } 544 }
  545 +
377 } 546 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import akka.actor.ActorRef;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.msg.MsgType;
  22 +import org.thingsboard.server.common.msg.TbActorMsg;
  23 +import org.thingsboard.server.common.msg.TbMsg;
  24 +
  25 +/**
  26 + * Created by ashvayka on 15.03.18.
  27 + */
  28 +@Data
  29 +public final class DeviceActorToRuleEngineMsg implements TbActorMsg {
  30 +
  31 + private final ActorRef callbackRef;
  32 + private final TbMsg tbMsg;
  33 +
  34 + @Override
  35 + public MsgType getMsgType() {
  36 + return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG;
  37 + }
  38 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.SessionId;
  21 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  22 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  23 +
  24 +import java.util.Optional;
  25 +
  26 +/**
  27 + * Created by ashvayka on 17.04.18.
  28 + */
  29 +@Data
  30 +@AllArgsConstructor
  31 +public final class PendingSessionMsgData {
  32 +
  33 + private final SessionId sessionId;
  34 + private final Optional<ServerAddress> serverAddress;
  35 + private final SessionMsgType sessionMsgType;
  36 + private final int requestId;
  37 + private final boolean replyOnQueueAck;
  38 + private int ackMsgCount;
  39 +
  40 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.msg.MsgType;
  20 +import org.thingsboard.server.common.msg.TbActorMsg;
  21 +
  22 +import java.util.UUID;
  23 +
  24 +/**
  25 + * Created by ashvayka on 15.03.18.
  26 + */
  27 +@Data
  28 +public final class RuleEngineQueuePutAckMsg implements TbActorMsg {
  29 +
  30 + private final UUID id;
  31 +
  32 + @Override
  33 + public MsgType getMsgType() {
  34 + return MsgType.RULE_ENGINE_QUEUE_PUT_ACK_MSG;
  35 + }
  36 +}
@@ -16,13 +16,13 @@ @@ -16,13 +16,13 @@
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; 19 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
20 20
21 /** 21 /**
22 * @author Andrew Shvayka 22 * @author Andrew Shvayka
23 */ 23 */
24 @Data 24 @Data
25 public class ToDeviceRpcRequestMetadata { 25 public class ToDeviceRpcRequestMetadata {
26 - private final ToDeviceRpcRequestPluginMsg msg; 26 + private final ToDeviceRpcRequestActorMsg msg;
27 private final boolean sent; 27 private final boolean sent;
28 } 28 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.device;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.SessionId;
  20 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  21 +import org.thingsboard.server.common.msg.session.SessionType;
  22 +
  23 +import java.util.Optional;
  24 +
  25 +/**
  26 + * @author Andrew Shvayka
  27 + */
  28 +@Data
  29 +public class ToServerRpcRequestMetadata {
  30 + private final SessionId sessionId;
  31 + private final SessionType type;
  32 + private final Optional<ServerAddress> server;
  33 +}
@@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.msg.TbActorMsg; 26 import org.thingsboard.server.common.msg.TbActorMsg;
27 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 27 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
28 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 28 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
29 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; 29 +import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
30 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; 30 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
31 import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; 31 import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
32 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; 32 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
@@ -153,6 +153,7 @@ public class PluginActor extends ComponentActor<PluginId, PluginActorMessageProc @@ -153,6 +153,7 @@ public class PluginActor extends ComponentActor<PluginId, PluginActorMessageProc
153 153
154 @Override 154 @Override
155 protected long getErrorPersistFrequency() { 155 protected long getErrorPersistFrequency() {
156 - return systemContext.getPluginErrorPersistFrequency(); 156 + return 0;
  157 +// return systemContext.getPluginErrorPersistFrequency();
157 } 158 }
158 } 159 }
@@ -30,13 +30,14 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; @@ -30,13 +30,14 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
30 import org.thingsboard.server.common.msg.cluster.ServerAddress; 30 import org.thingsboard.server.common.msg.cluster.ServerAddress;
31 import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; 31 import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
32 import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; 32 import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
33 -import org.thingsboard.server.common.msg.session.MsgType; 33 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  34 +import org.thingsboard.server.common.msg.session.SessionMsgType;
34 import org.thingsboard.server.extensions.api.plugins.Plugin; 35 import org.thingsboard.server.extensions.api.plugins.Plugin;
35 import org.thingsboard.server.extensions.api.plugins.PluginInitializationException; 36 import org.thingsboard.server.extensions.api.plugins.PluginInitializationException;
36 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; 37 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
37 import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; 38 import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;
38 import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; 39 import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
39 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; 40 +import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
40 import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg; 41 import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
41 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; 42 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
42 import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg; 43 import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
@@ -108,7 +109,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId> @@ -108,7 +109,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId>
108 } catch (Exception ex) { 109 } catch (Exception ex) {
109 logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex); 110 logger.debug("[{}] Failed to process RuleToPlugin msg: [{}] [{}]", tenantId, msg.getMsg(), ex);
110 RuleToPluginMsg ruleMsg = msg.getMsg(); 111 RuleToPluginMsg ruleMsg = msg.getMsg();
111 - MsgType responceMsgType = MsgType.RULE_ENGINE_ERROR; 112 + SessionMsgType responceMsgType = SessionMsgType.RULE_ENGINE_ERROR;
112 Integer requestId = 0; 113 Integer requestId = 0;
113 if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) { 114 if (ruleMsg.getPayload() instanceof FromDeviceRequestMsg) {
114 requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId(); 115 requestId = ((FromDeviceRequestMsg) ruleMsg.getPayload()).getRequestId();
@@ -216,7 +217,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId> @@ -216,7 +217,7 @@ public class PluginActorMessageProcessor extends ComponentMsgProcessor<PluginId>
216 @Override 217 @Override
217 public void onStop(ActorContext context) { 218 public void onStop(ActorContext context) {
218 onStop(); 219 onStop();
219 - scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay()); 220 +// scheduleMsgWithDelay(context, new PluginTerminationMsg(entityId), systemContext.getPluginActorTerminationDelay());
220 } 221 }
221 222
222 private void onStop() { 223 private void onStop() {
@@ -36,9 +36,12 @@ import org.thingsboard.server.common.data.page.TextPageLink; @@ -36,9 +36,12 @@ import org.thingsboard.server.common.data.page.TextPageLink;
36 import org.thingsboard.server.common.data.plugin.PluginMetaData; 36 import org.thingsboard.server.common.data.plugin.PluginMetaData;
37 import org.thingsboard.server.common.data.relation.EntityRelation; 37 import org.thingsboard.server.common.data.relation.EntityRelation;
38 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 38 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  39 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
39 import org.thingsboard.server.common.data.rule.RuleChain; 40 import org.thingsboard.server.common.data.rule.RuleChain;
40 import org.thingsboard.server.common.data.rule.RuleMetaData; 41 import org.thingsboard.server.common.data.rule.RuleMetaData;
41 import org.thingsboard.server.common.msg.cluster.ServerAddress; 42 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  43 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  44 +import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
42 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; 45 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
43 import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext; 46 import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
44 import org.thingsboard.server.extensions.api.plugins.PluginCallback; 47 import org.thingsboard.server.extensions.api.plugins.PluginCallback;
@@ -348,7 +351,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -348,7 +351,7 @@ public final class PluginProcessingContext implements PluginContext {
348 throw new IllegalStateException("Not Implemented!"); 351 throw new IllegalStateException("Not Implemented!");
349 } 352 }
350 } else { 353 } else {
351 - callback.onSuccess(this, ValidationResult.ok()); 354 + callback.onSuccess(this, ValidationResult.ok(null));
352 } 355 }
353 } 356 }
354 357
@@ -366,7 +369,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -366,7 +369,7 @@ public final class PluginProcessingContext implements PluginContext {
366 } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) { 369 } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
367 return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); 370 return ValidationResult.accessDenied("Device doesn't belong to the current Customer!");
368 } else { 371 } else {
369 - return ValidationResult.ok(); 372 + return ValidationResult.ok(null);
370 } 373 }
371 } 374 }
372 })); 375 }));
@@ -387,7 +390,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -387,7 +390,7 @@ public final class PluginProcessingContext implements PluginContext {
387 } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) { 390 } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
388 return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); 391 return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");
389 } else { 392 } else {
390 - return ValidationResult.ok(); 393 + return ValidationResult.ok(null);
391 } 394 }
392 } 395 }
393 })); 396 }));
@@ -408,7 +411,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -408,7 +411,7 @@ public final class PluginProcessingContext implements PluginContext {
408 } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) { 411 } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) {
409 return ValidationResult.accessDenied("Rule is not in system scope!"); 412 return ValidationResult.accessDenied("Rule is not in system scope!");
410 } else { 413 } else {
411 - return ValidationResult.ok(); 414 + return ValidationResult.ok(null);
412 } 415 }
413 } 416 }
414 })); 417 }));
@@ -429,7 +432,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -429,7 +432,7 @@ public final class PluginProcessingContext implements PluginContext {
429 } else if (ctx.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { 432 } else if (ctx.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
430 return ValidationResult.accessDenied("Rule chain is not in system scope!"); 433 return ValidationResult.accessDenied("Rule chain is not in system scope!");
431 } else { 434 } else {
432 - return ValidationResult.ok(); 435 + return ValidationResult.ok(null);
433 } 436 }
434 } 437 }
435 })); 438 }));
@@ -451,7 +454,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -451,7 +454,7 @@ public final class PluginProcessingContext implements PluginContext {
451 } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) { 454 } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) {
452 return ValidationResult.accessDenied("Plugin is not in system scope!"); 455 return ValidationResult.accessDenied("Plugin is not in system scope!");
453 } else { 456 } else {
454 - return ValidationResult.ok(); 457 + return ValidationResult.ok(null);
455 } 458 }
456 } 459 }
457 })); 460 }));
@@ -472,7 +475,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -472,7 +475,7 @@ public final class PluginProcessingContext implements PluginContext {
472 } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) { 475 } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
473 return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); 476 return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");
474 } else { 477 } else {
475 - return ValidationResult.ok(); 478 + return ValidationResult.ok(null);
476 } 479 }
477 } 480 }
478 })); 481 }));
@@ -483,7 +486,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -483,7 +486,7 @@ public final class PluginProcessingContext implements PluginContext {
483 if (ctx.isCustomerUser()) { 486 if (ctx.isCustomerUser()) {
484 callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 487 callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
485 } else if (ctx.isSystemAdmin()) { 488 } else if (ctx.isSystemAdmin()) {
486 - callback.onSuccess(this, ValidationResult.ok()); 489 + callback.onSuccess(this, ValidationResult.ok(null));
487 } else { 490 } else {
488 ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); 491 ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
489 Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { 492 Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
@@ -492,7 +495,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -492,7 +495,7 @@ public final class PluginProcessingContext implements PluginContext {
492 } else if (!tenant.getId().equals(ctx.getTenantId())) { 495 } else if (!tenant.getId().equals(ctx.getTenantId())) {
493 return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); 496 return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
494 } else { 497 } else {
495 - return ValidationResult.ok(); 498 + return ValidationResult.ok(null);
496 } 499 }
497 })); 500 }));
498 } 501 }
@@ -18,13 +18,13 @@ package org.thingsboard.server.actors.plugin; @@ -18,13 +18,13 @@ package org.thingsboard.server.actors.plugin;
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
21 -import org.thingsboard.server.common.data.Device;  
22 import org.thingsboard.server.common.data.id.DeviceId; 21 import org.thingsboard.server.common.data.id.DeviceId;
23 -import org.thingsboard.server.common.data.id.EntityId; 22 +import org.thingsboard.server.common.data.id.PluginId;
24 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
25 import org.thingsboard.server.common.msg.cluster.ServerAddress; 24 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  25 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  26 +import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
26 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; 27 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
27 -import org.thingsboard.server.common.data.id.PluginId;  
28 import org.thingsboard.server.dao.asset.AssetService; 28 import org.thingsboard.server.dao.asset.AssetService;
29 import org.thingsboard.server.dao.attributes.AttributesService; 29 import org.thingsboard.server.dao.attributes.AttributesService;
30 import org.thingsboard.server.dao.audit.AuditLogService; 30 import org.thingsboard.server.dao.audit.AuditLogService;
@@ -37,9 +37,6 @@ import org.thingsboard.server.dao.rule.RuleService; @@ -37,9 +37,6 @@ import org.thingsboard.server.dao.rule.RuleService;
37 import org.thingsboard.server.dao.tenant.TenantService; 37 import org.thingsboard.server.dao.tenant.TenantService;
38 import org.thingsboard.server.dao.timeseries.TimeseriesService; 38 import org.thingsboard.server.dao.timeseries.TimeseriesService;
39 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; 39 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
40 -import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;  
41 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;  
42 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;  
43 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; 40 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
44 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; 41 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
45 import scala.concurrent.duration.Duration; 42 import scala.concurrent.duration.Duration;
@@ -108,8 +105,8 @@ public final class SharedPluginProcessingContext { @@ -108,8 +105,8 @@ public final class SharedPluginProcessingContext {
108 105
109 public void sendRpcRequest(ToDeviceRpcRequest msg) { 106 public void sendRpcRequest(ToDeviceRpcRequest msg) {
110 log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg); 107 log.trace("[{}] Forwarding msg {} to device actor!", pluginId, msg);
111 - ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg);  
112 - forward(msg.getDeviceId(), rpcMsg, rpcService::tell); 108 +// ToDeviceRpcRequestPluginMsg rpcMsg = new ToDeviceRpcRequestPluginMsg(pluginId, tenantId, msg);
  109 +// forward(msg.getDeviceId(), rpcMsg, rpcService::tell);
113 } 110 }
114 111
115 private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) { 112 private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {
@@ -20,29 +20,30 @@ import lombok.Data; @@ -20,29 +20,30 @@ import lombok.Data;
20 20
21 @Data 21 @Data
22 @AllArgsConstructor 22 @AllArgsConstructor
23 -public class ValidationResult { 23 +public class ValidationResult<V> {
24 24
25 private final ValidationResultCode resultCode; 25 private final ValidationResultCode resultCode;
26 private final String message; 26 private final String message;
  27 + private final V v;
27 28
28 - public static ValidationResult ok() {  
29 - return new ValidationResult(ValidationResultCode.OK, "Ok"); 29 + public static <V> ValidationResult<V> ok(V v) {
  30 + return new ValidationResult<>(ValidationResultCode.OK, "Ok", v);
30 } 31 }
31 32
32 - public static ValidationResult accessDenied(String message) {  
33 - return new ValidationResult(ValidationResultCode.ACCESS_DENIED, message); 33 + public static <V> ValidationResult<V> accessDenied(String message) {
  34 + return new ValidationResult<>(ValidationResultCode.ACCESS_DENIED, message, null);
34 } 35 }
35 36
36 - public static ValidationResult entityNotFound(String message) {  
37 - return new ValidationResult(ValidationResultCode.ENTITY_NOT_FOUND, message); 37 + public static <V> ValidationResult<V> entityNotFound(String message) {
  38 + return new ValidationResult<>(ValidationResultCode.ENTITY_NOT_FOUND, message, null);
38 } 39 }
39 40
40 - public static ValidationResult unauthorized(String message) {  
41 - return new ValidationResult(ValidationResultCode.UNAUTHORIZED, message); 41 + public static <V> ValidationResult<V> unauthorized(String message) {
  42 + return new ValidationResult<>(ValidationResultCode.UNAUTHORIZED, message, null);
42 } 43 }
43 44
44 - public static ValidationResult internalError(String message) {  
45 - return new ValidationResult(ValidationResultCode.INTERNAL_ERROR, message); 45 + public static <V> ValidationResult<V> internalError(String message) {
  46 + return new ValidationResult<>(ValidationResultCode.INTERNAL_ERROR, message, null);
46 } 47 }
47 48
48 } 49 }
@@ -24,10 +24,12 @@ import org.thingsboard.server.actors.service.ActorService; @@ -24,10 +24,12 @@ import org.thingsboard.server.actors.service.ActorService;
24 import org.thingsboard.server.common.data.id.DeviceId; 24 import org.thingsboard.server.common.data.id.DeviceId;
25 import org.thingsboard.server.common.data.id.PluginId; 25 import org.thingsboard.server.common.data.id.PluginId;
26 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
27 import org.thingsboard.server.common.msg.cluster.ServerAddress; 28 import org.thingsboard.server.common.msg.cluster.ServerAddress;
28 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; 29 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
29 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; 30 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
30 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 31 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
  32 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
31 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 33 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
32 import org.thingsboard.server.extensions.api.plugins.msg.*; 34 import org.thingsboard.server.extensions.api.plugins.msg.*;
33 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; 35 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
@@ -35,6 +37,7 @@ import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg; @@ -35,6 +37,7 @@ import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;
35 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 37 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
36 import org.thingsboard.server.service.cluster.rpc.GrpcSession; 38 import org.thingsboard.server.service.cluster.rpc.GrpcSession;
37 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener; 39 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
  40 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
38 41
39 import java.io.Serializable; 42 import java.io.Serializable;
40 import java.util.UUID; 43 import java.util.UUID;
@@ -83,7 +86,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener { @@ -83,7 +86,7 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
83 @Override 86 @Override
84 public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) { 87 public void onToDeviceActorRpcMsg(GrpcSession session, ClusterAPIProtos.ToDeviceActorRpcMessage msg) {
85 log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg); 88 log.trace("{} session [{}] received device actor msg {}", getType(session), session.getRemoteServer(), msg);
86 - service.onMsg((ToDeviceActorMsg) deserialize(msg.getData().toByteArray())); 89 + service.onMsg((DeviceToDeviceActorMsg) deserialize(msg.getData().toByteArray()));
87 } 90 }
88 91
89 @Override 92 @Override
@@ -139,28 +142,20 @@ public class BasicRpcSessionListener implements GrpcSessionListener { @@ -139,28 +142,20 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
139 return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb()); 142 return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb());
140 } 143 }
141 144
142 - private static ToDeviceRpcRequestPluginMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {  
143 - ClusterAPIProtos.PluginAddress address = msg.getAddress();  
144 - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));  
145 - PluginId pluginId = new PluginId(toUUID(address.getPluginId()));  
146 - 145 + private static ToDeviceRpcRequestActorMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
147 TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId())); 146 TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId()));
148 DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId())); 147 DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId()));
149 148
150 ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams()); 149 ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams());
151 - ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), null, deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody); 150 + ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody);
152 151
153 - return new ToDeviceRpcRequestPluginMsg(serverAddress, pluginId, pluginTenantId, request); 152 + return new ToDeviceRpcRequestActorMsg(serverAddress, request);
154 } 153 }
155 154
156 private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) { 155 private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
157 - ClusterAPIProtos.PluginAddress address = msg.getAddress();  
158 - TenantId pluginTenantId = new TenantId(toUUID(address.getTenantId()));  
159 - PluginId pluginId = new PluginId(toUUID(address.getPluginId()));  
160 -  
161 RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null; 156 RpcError error = !StringUtils.isEmpty(msg.getError()) ? RpcError.valueOf(msg.getError()) : null;
162 FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error); 157 FromDeviceRpcResponse response = new FromDeviceRpcResponse(toUUID(msg.getMsgId()), msg.getResponse(), error);
163 - return new ToPluginRpcResponseDeviceMsg(pluginId, pluginTenantId, response); 158 + return new ToPluginRpcResponseDeviceMsg(null, null, response);
164 } 159 }
165 160
166 @SuppressWarnings("unchecked") 161 @SuppressWarnings("unchecked")
@@ -16,15 +16,20 @@ @@ -16,15 +16,20 @@
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 -import akka.actor.Cancellable; 19 +import com.datastax.driver.core.utils.UUIDs;
20 import com.google.common.base.Function; 20 import com.google.common.base.Function;
21 import org.thingsboard.rule.engine.api.*; 21 import org.thingsboard.rule.engine.api.*;
22 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.RuleNodeId; 25 import org.thingsboard.server.common.data.id.RuleNodeId;
24 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
25 import org.thingsboard.server.common.data.rule.RuleNode; 28 import org.thingsboard.server.common.data.rule.RuleNode;
26 import org.thingsboard.server.common.msg.TbMsg; 29 import org.thingsboard.server.common.msg.TbMsg;
  30 +import org.thingsboard.server.common.msg.TbMsgMetaData;
27 import org.thingsboard.server.common.msg.cluster.ServerAddress; 31 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  32 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
28 import org.thingsboard.server.dao.alarm.AlarmService; 33 import org.thingsboard.server.dao.alarm.AlarmService;
29 import org.thingsboard.server.dao.asset.AssetService; 34 import org.thingsboard.server.dao.asset.AssetService;
30 import org.thingsboard.server.dao.attributes.AttributesService; 35 import org.thingsboard.server.dao.attributes.AttributesService;
@@ -41,13 +46,13 @@ import scala.concurrent.duration.Duration; @@ -41,13 +46,13 @@ import scala.concurrent.duration.Duration;
41 import java.util.List; 46 import java.util.List;
42 import java.util.Set; 47 import java.util.Set;
43 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.TimeUnit;
  49 +import java.util.function.Consumer;
44 50
45 /** 51 /**
46 * Created by ashvayka on 19.03.18. 52 * Created by ashvayka on 19.03.18.
47 */ 53 */
48 class DefaultTbContext implements TbContext { 54 class DefaultTbContext implements TbContext {
49 55
50 - private static final Function<? super List<Void>, ? extends Void> LIST_VOID_FUNCTION = v -> null;  
51 private final ActorSystemContext mainCtx; 56 private final ActorSystemContext mainCtx;
52 private final RuleNodeCtx nodeCtx; 57 private final RuleNodeCtx nodeCtx;
53 58
@@ -63,8 +68,13 @@ class DefaultTbContext implements TbContext { @@ -63,8 +68,13 @@ class DefaultTbContext implements TbContext {
63 68
64 @Override 69 @Override
65 public void tellNext(TbMsg msg, String relationType) { 70 public void tellNext(TbMsg msg, String relationType) {
  71 + tellNext(msg, relationType, null);
  72 + }
  73 +
  74 + @Override
  75 + public void tellNext(TbMsg msg, String relationType, Throwable th) {
66 if (nodeCtx.getSelf().isDebugMode()) { 76 if (nodeCtx.getSelf().isDebugMode()) {
67 - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg); 77 + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, th);
68 } 78 }
69 nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationType, msg), nodeCtx.getSelfActor()); 79 nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationType, msg), nodeCtx.getSelfActor());
70 } 80 }
@@ -113,6 +123,16 @@ class DefaultTbContext implements TbContext { @@ -113,6 +123,16 @@ class DefaultTbContext implements TbContext {
113 } 123 }
114 124
115 @Override 125 @Override
  126 + public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
  127 + return new TbMsg(UUIDs.timeBased(), type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), 0L);
  128 + }
  129 +
  130 + @Override
  131 + public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
  132 + return new TbMsg(origMsg.getId(), type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), 0L);
  133 + }
  134 +
  135 + @Override
116 public RuleNodeId getSelfId() { 136 public RuleNodeId getSelfId() {
117 return nodeCtx.getSelf().getId(); 137 return nodeCtx.getSelf().getId();
118 } 138 }
@@ -143,6 +163,11 @@ class DefaultTbContext implements TbContext { @@ -143,6 +163,11 @@ class DefaultTbContext implements TbContext {
143 } 163 }
144 164
145 @Override 165 @Override
  166 + public ListeningExecutor getExternalCallExecutor() {
  167 + return mainCtx.getExternalCallExecutorService();
  168 + }
  169 +
  170 + @Override
146 public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) { 171 public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) {
147 return new NashornJsEngine(script, functionName, argNames); 172 return new NashornJsEngine(script, functionName, argNames);
148 } 173 }
@@ -206,4 +231,28 @@ class DefaultTbContext implements TbContext { @@ -206,4 +231,28 @@ class DefaultTbContext implements TbContext {
206 public MailService getMailService() { 231 public MailService getMailService() {
207 return mainCtx.getMailService(); 232 return mainCtx.getMailService();
208 } 233 }
  234 +
  235 + @Override
  236 + public RuleEngineRpcService getRpcService() {
  237 + return new RuleEngineRpcService() {
  238 + @Override
  239 + public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
  240 + mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
  241 + }
  242 +
  243 + @Override
  244 + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
  245 + ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(),
  246 + src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
  247 + mainCtx.getDeviceRpcService().process(request, response -> {
  248 + consumer.accept(RuleEngineDeviceRpcResponse.builder()
  249 + .deviceId(src.getDeviceId())
  250 + .requestId(src.getRequestId())
  251 + .error(response.getError())
  252 + .response(response.getResponse())
  253 + .build());
  254 + });
  255 + }
  256 + };
  257 + }
209 } 258 }
@@ -18,6 +18,7 @@ package org.thingsboard.server.actors.ruleChain; @@ -18,6 +18,7 @@ package org.thingsboard.server.actors.ruleChain;
18 import akka.actor.OneForOneStrategy; 18 import akka.actor.OneForOneStrategy;
19 import akka.actor.SupervisorStrategy; 19 import akka.actor.SupervisorStrategy;
20 import org.thingsboard.server.actors.ActorSystemContext; 20 import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
21 import org.thingsboard.server.actors.service.ComponentActor; 22 import org.thingsboard.server.actors.service.ComponentActor;
22 import org.thingsboard.server.actors.service.ContextBasedCreator; 23 import org.thingsboard.server.actors.service.ContextBasedCreator;
23 import org.thingsboard.server.common.data.id.RuleChainId; 24 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -44,9 +45,15 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -44,9 +45,15 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
44 case SERVICE_TO_RULE_ENGINE_MSG: 45 case SERVICE_TO_RULE_ENGINE_MSG:
45 processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); 46 processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
46 break; 47 break;
  48 + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
  49 + processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
  50 + break;
47 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: 51 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
48 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); 52 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
49 break; 53 break;
  54 + case RULE_CHAIN_TO_RULE_CHAIN_MSG:
  55 + processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
  56 + break;
50 default: 57 default:
51 return false; 58 return false;
52 } 59 }
@@ -19,7 +19,10 @@ import akka.actor.ActorContext; @@ -19,7 +19,10 @@ import akka.actor.ActorContext;
19 import akka.actor.ActorRef; 19 import akka.actor.ActorRef;
20 import akka.actor.Props; 20 import akka.actor.Props;
21 import akka.event.LoggingAdapter; 21 import akka.event.LoggingAdapter;
  22 +import com.datastax.driver.core.utils.UUIDs;
22 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
  24 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
  25 +import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
23 import org.thingsboard.server.actors.service.DefaultActorService; 26 import org.thingsboard.server.actors.service.DefaultActorService;
24 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 27 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
25 import org.thingsboard.server.common.data.EntityType; 28 import org.thingsboard.server.common.data.EntityType;
@@ -39,6 +42,7 @@ import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; @@ -39,6 +42,7 @@ import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
39 import org.thingsboard.server.dao.rule.RuleChainService; 42 import org.thingsboard.server.dao.rule.RuleChainService;
40 43
41 import java.util.ArrayList; 44 import java.util.ArrayList;
  45 +import java.util.Collections;
42 import java.util.HashMap; 46 import java.util.HashMap;
43 import java.util.List; 47 import java.util.List;
44 import java.util.Map; 48 import java.util.Map;
@@ -50,6 +54,7 @@ import java.util.stream.Collectors; @@ -50,6 +54,7 @@ import java.util.stream.Collectors;
50 */ 54 */
51 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> { 55 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
52 56
  57 + private static final long DEFAULT_CLUSTER_PARTITION = 0L;
53 private final ActorRef parent; 58 private final ActorRef parent;
54 private final ActorRef self; 59 private final ActorRef self;
55 private final Map<RuleNodeId, RuleNodeCtx> nodeActors; 60 private final Map<RuleNodeId, RuleNodeCtx> nodeActors;
@@ -58,6 +63,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -58,6 +63,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
58 63
59 private RuleNodeId firstId; 64 private RuleNodeId firstId;
60 private RuleNodeCtx firstNode; 65 private RuleNodeCtx firstNode;
  66 + private boolean started;
61 67
62 RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext 68 RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
63 , LoggingAdapter logger, ActorRef parent, ActorRef self) { 69 , LoggingAdapter logger, ActorRef parent, ActorRef self) {
@@ -71,14 +77,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -71,14 +77,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
71 77
72 @Override 78 @Override
73 public void start(ActorContext context) throws Exception { 79 public void start(ActorContext context) throws Exception {
74 - RuleChain ruleChain = service.findRuleChainById(entityId);  
75 - List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);  
76 - // Creating and starting the actors; 80 + if (!started) {
  81 + RuleChain ruleChain = service.findRuleChainById(entityId);
  82 + List<RuleNode> ruleNodeList = service.getRuleChainNodes(entityId);
  83 + // Creating and starting the actors;
  84 + for (RuleNode ruleNode : ruleNodeList) {
  85 + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);
  86 + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode));
  87 + }
  88 + initRoutes(ruleChain, ruleNodeList);
  89 + reprocess(ruleNodeList);
  90 + started = true;
  91 + } else {
  92 + onUpdate(context);
  93 + }
  94 + }
  95 +
  96 + private void reprocess(List<RuleNode> ruleNodeList) {
77 for (RuleNode ruleNode : ruleNodeList) { 97 for (RuleNode ruleNode : ruleNodeList) {
78 - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode);  
79 - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); 98 + for (TbMsg tbMsg : queue.findUnprocessed(ruleNode.getId().getId(), 0L)) {
  99 + pushMsgToNode(nodeActors.get(ruleNode.getId()), tbMsg);
  100 + }
  101 + }
  102 + if (firstNode != null) {
  103 + for (TbMsg tbMsg : queue.findUnprocessed(entityId.getId(), 0L)) {
  104 + pushMsgToNode(firstNode, tbMsg);
  105 + }
80 } 106 }
81 - initRoutes(ruleChain, ruleNodeList);  
82 } 107 }
83 108
84 @Override 109 @Override
@@ -105,6 +130,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -105,6 +130,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
105 }); 130 });
106 131
107 initRoutes(ruleChain, ruleNodeList); 132 initRoutes(ruleChain, ruleNodeList);
  133 + reprocess(ruleNodeList);
108 } 134 }
109 135
110 @Override 136 @Override
@@ -113,6 +139,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -113,6 +139,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
113 nodeActors.clear(); 139 nodeActors.clear();
114 nodeRoutes.clear(); 140 nodeRoutes.clear();
115 context.stop(self); 141 context.stop(self);
  142 + started = false;
116 } 143 }
117 144
118 @Override 145 @Override
@@ -133,15 +160,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -133,15 +160,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
133 // Populating the routes map; 160 // Populating the routes map;
134 for (RuleNode ruleNode : ruleNodeList) { 161 for (RuleNode ruleNode : ruleNodeList) {
135 List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId()); 162 List<EntityRelation> relations = service.getRuleNodeRelations(ruleNode.getId());
136 - for (EntityRelation relation : relations) {  
137 - if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {  
138 - RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));  
139 - if (ruleNodeCtx == null) {  
140 - throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]"); 163 + if (relations.size() == 0) {
  164 + nodeRoutes.put(ruleNode.getId(), Collections.emptyList());
  165 + } else {
  166 + for (EntityRelation relation : relations) {
  167 + if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
  168 + RuleNodeCtx ruleNodeCtx = nodeActors.get(new RuleNodeId(relation.getTo().getId()));
  169 + if (ruleNodeCtx == null) {
  170 + throw new IllegalArgumentException("Rule Node [" + relation.getFrom() + "] has invalid relation to Rule node [" + relation.getTo() + "]");
  171 + }
141 } 172 }
  173 + nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>())
  174 + .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType()));
142 } 175 }
143 - nodeRoutes.computeIfAbsent(ruleNode.getId(), k -> new ArrayList<>())  
144 - .add(new RuleNodeRelation(ruleNode.getId(), relation.getTo(), relation.getType()));  
145 } 176 }
146 } 177 }
147 178
@@ -152,37 +183,81 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -152,37 +183,81 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
152 183
153 void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) { 184 void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
154 checkActive(); 185 checkActive();
155 - TbMsg tbMsg = envelope.getTbMsg();  
156 - //TODO: push to queue and act on ack in async way  
157 - pushMsgToNode(firstNode, tbMsg); 186 + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> pushMsgToNode(firstNode, msg));
  187 + }
  188 +
  189 + void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
  190 + checkActive();
  191 + putToQueue(enrichWithRuleChainId(envelope.getTbMsg()), msg -> {
  192 + pushMsgToNode(firstNode, msg);
  193 + envelope.getCallbackRef().tell(new RuleEngineQueuePutAckMsg(msg.getId()), self);
  194 + });
  195 + }
  196 +
  197 + void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) {
  198 + checkActive();
  199 + if (envelope.isEnqueue()) {
  200 + putToQueue(enrichWithRuleChainId(envelope.getMsg()), msg -> pushMsgToNode(firstNode, msg));
  201 + } else {
  202 + pushMsgToNode(firstNode, envelope.getMsg());
  203 + }
158 } 204 }
159 205
160 void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { 206 void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
161 checkActive(); 207 checkActive();
162 RuleNodeId originator = envelope.getOriginator(); 208 RuleNodeId originator = envelope.getOriginator();
163 String targetRelationType = envelope.getRelationType(); 209 String targetRelationType = envelope.getRelationType();
164 - List<RuleNodeRelation> relations = nodeRoutes.get(originator);  
165 - if (relations == null) {  
166 - return;  
167 - }  
168 - boolean copy = relations.size() > 1;  
169 - for (RuleNodeRelation relation : relations) {  
170 - TbMsg msg = envelope.getMsg();  
171 - if (copy) {  
172 - msg = msg.copy(); 210 + List<RuleNodeRelation> relations = nodeRoutes.get(originator).stream()
  211 + .filter(r -> targetRelationType == null || targetRelationType.equalsIgnoreCase(r.getType()))
  212 + .collect(Collectors.toList());
  213 +
  214 + TbMsg msg = envelope.getMsg();
  215 + int relationsCount = relations.size();
  216 + EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
  217 + if (relationsCount == 0) {
  218 + queue.ack(msg, ackId.getId(), msg.getClusterPartition());
  219 + } else if (relationsCount == 1) {
  220 + for (RuleNodeRelation relation : relations) {
  221 + pushToTarget(msg, relation.getOut());
173 } 222 }
174 - if (targetRelationType == null || targetRelationType.equalsIgnoreCase(relation.getType())) {  
175 - switch (relation.getOut().getEntityType()) { 223 + } else {
  224 + for (RuleNodeRelation relation : relations) {
  225 + EntityId target = relation.getOut();
  226 + switch (target.getEntityType()) {
176 case RULE_NODE: 227 case RULE_NODE:
177 - RuleNodeId targetRuleNodeId = new RuleNodeId(relation.getOut().getId());  
178 - RuleNodeCtx targetRuleNode = nodeActors.get(targetRuleNodeId);  
179 - pushMsgToNode(targetRuleNode, msg); 228 + enqueueAndForwardMsgCopyToNode(msg, target);
180 break; 229 break;
181 case RULE_CHAIN: 230 case RULE_CHAIN:
182 -// TODO: implement 231 + enqueueAndForwardMsgCopyToChain(msg, target);
183 break; 232 break;
184 } 233 }
185 } 234 }
  235 + //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
  236 + queue.ack(msg, ackId.getId(), msg.getClusterPartition());
  237 + }
  238 + }
  239 +
  240 + private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target) {
  241 + RuleChainId targetRCId = new RuleChainId(target.getId());
  242 + TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION);
  243 + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, copyMsg, true), self);
  244 + }
  245 +
  246 + private void enqueueAndForwardMsgCopyToNode(TbMsg msg, EntityId target) {
  247 + RuleNodeId targetId = new RuleNodeId(target.getId());
  248 + RuleNodeCtx targetNodeCtx = nodeActors.get(targetId);
  249 + TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION);
  250 + putToQueue(copy, queuedMsg -> pushMsgToNode(targetNodeCtx, queuedMsg));
  251 + }
  252 +
  253 + private void pushToTarget(TbMsg msg, EntityId target) {
  254 + switch (target.getEntityType()) {
  255 + case RULE_NODE:
  256 + pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg);
  257 + break;
  258 + case RULE_CHAIN:
  259 + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, false), self);
  260 + break;
186 } 261 }
187 } 262 }
188 263
@@ -192,4 +267,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -192,4 +267,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
192 } 267 }
193 } 268 }
194 269
  270 + private TbMsg enrichWithRuleChainId(TbMsg tbMsg) {
  271 + // We don't put firstNodeId because it may change over time;
  272 + return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData(), tbMsg.getData(), entityId, null, 0L);
  273 + }
195 } 274 }
@@ -31,31 +31,29 @@ import org.thingsboard.server.dao.rule.RuleChainService; @@ -31,31 +31,29 @@ import org.thingsboard.server.dao.rule.RuleChainService;
31 public abstract class RuleChainManagerActor extends ContextAwareActor { 31 public abstract class RuleChainManagerActor extends ContextAwareActor {
32 32
33 protected final RuleChainManager ruleChainManager; 33 protected final RuleChainManager ruleChainManager;
34 - protected final PluginManager pluginManager;  
35 protected final RuleChainService ruleChainService; 34 protected final RuleChainService ruleChainService;
36 35
37 public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager, PluginManager pluginManager) { 36 public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager, PluginManager pluginManager) {
38 super(systemContext); 37 super(systemContext);
39 this.ruleChainManager = ruleChainManager; 38 this.ruleChainManager = ruleChainManager;
40 - this.pluginManager = pluginManager;  
41 this.ruleChainService = systemContext.getRuleChainService(); 39 this.ruleChainService = systemContext.getRuleChainService();
42 } 40 }
43 41
44 protected void initRuleChains() { 42 protected void initRuleChains() {
45 - pluginManager.init(this.context());  
46 ruleChainManager.init(this.context()); 43 ruleChainManager.init(this.context());
47 } 44 }
48 45
49 protected ActorRef getEntityActorRef(EntityId entityId) { 46 protected ActorRef getEntityActorRef(EntityId entityId) {
50 ActorRef target = null; 47 ActorRef target = null;
51 switch (entityId.getEntityType()) { 48 switch (entityId.getEntityType()) {
52 - case PLUGIN:  
53 - target = pluginManager.getOrCreateActor(this.context(), (PluginId) entityId);  
54 - break;  
55 case RULE_CHAIN: 49 case RULE_CHAIN:
56 target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId); 50 target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId);
57 break; 51 break;
58 } 52 }
59 return target; 53 return target;
60 } 54 }
  55 +
  56 + protected void broadcast(Object msg) {
  57 + ruleChainManager.broadcast(msg);
  58 + }
61 } 59 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.ruleChain;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.TbContext;
  20 +import org.thingsboard.server.common.data.id.RuleChainId;
  21 +import org.thingsboard.server.common.msg.MsgType;
  22 +import org.thingsboard.server.common.msg.TbActorMsg;
  23 +import org.thingsboard.server.common.msg.TbMsg;
  24 +
  25 +/**
  26 + * Created by ashvayka on 19.03.18.
  27 + */
  28 +@Data
  29 +public final class RuleChainToRuleChainMsg implements TbActorMsg {
  30 +
  31 + private final RuleChainId target;
  32 + private final RuleChainId source;
  33 + private final TbMsg msg;
  34 + private final boolean enqueue;
  35 +
  36 + @Override
  37 + public MsgType getMsgType() {
  38 + return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG;
  39 + }
  40 +}
@@ -32,4 +32,5 @@ public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor @@ -32,4 +32,5 @@ public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor
32 void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId); 32 void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId);
33 33
34 void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType); 34 void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType);
  35 +
35 } 36 }
@@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; @@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
39 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; 39 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
40 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 40 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
41 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg; 41 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
42 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 42 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
43 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 43 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
44 import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg; 44 import org.thingsboard.server.extensions.api.device.DeviceCredentialsUpdateNotificationMsg;
45 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 45 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
@@ -156,7 +156,7 @@ public class DefaultActorService implements ActorService { @@ -156,7 +156,7 @@ public class DefaultActorService implements ActorService {
156 } 156 }
157 157
158 @Override 158 @Override
159 - public void onMsg(ToDeviceActorMsg msg) { 159 + public void onMsg(DeviceToDeviceActorMsg msg) {
160 log.trace("Processing device rpc msg: {}", msg); 160 log.trace("Processing device rpc msg: {}", msg);
161 appActor.tell(msg, ActorRef.noSender()); 161 appActor.tell(msg, ActorRef.noSender());
162 } 162 }
@@ -22,12 +22,11 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; @@ -22,12 +22,11 @@ import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
22 import org.thingsboard.server.common.msg.cluster.ServerAddress; 22 import org.thingsboard.server.common.msg.cluster.ServerAddress;
23 import org.thingsboard.server.common.msg.core.*; 23 import org.thingsboard.server.common.msg.core.*;
24 import org.thingsboard.server.common.msg.core.SessionCloseMsg; 24 import org.thingsboard.server.common.msg.core.SessionCloseMsg;
25 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 25 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
26 import org.thingsboard.server.common.msg.session.*; 26 import org.thingsboard.server.common.msg.session.*;
27 27
28 import akka.actor.ActorContext; 28 import akka.actor.ActorContext;
29 import akka.event.LoggingAdapter; 29 import akka.event.LoggingAdapter;
30 -import org.thingsboard.server.common.msg.session.ctrl.*;  
31 import org.thingsboard.server.common.msg.session.ex.SessionException; 30 import org.thingsboard.server.common.msg.session.ex.SessionException;
32 31
33 import java.util.HashMap; 32 import java.util.HashMap;
@@ -37,7 +36,7 @@ import java.util.Optional; @@ -37,7 +36,7 @@ import java.util.Optional;
37 class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { 36 class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
38 37
39 private boolean firstMsg = true; 38 private boolean firstMsg = true;
40 - private Map<Integer, ToDeviceActorMsg> pendingMap = new HashMap<>(); 39 + private Map<Integer, DeviceToDeviceActorMsg> pendingMap = new HashMap<>();
41 private Optional<ServerAddress> currentTargetServer; 40 private Optional<ServerAddress> currentTargetServer;
42 private boolean subscribedToAttributeUpdates; 41 private boolean subscribedToAttributeUpdates;
43 private boolean subscribedToRpcCommands; 42 private boolean subscribedToRpcCommands;
@@ -53,7 +52,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { @@ -53,7 +52,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
53 toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m)); 52 toDeviceMsg(new SessionOpenMsg()).ifPresent(m -> forwardToAppActor(ctx, m));
54 firstMsg = false; 53 firstMsg = false;
55 } 54 }
56 - ToDeviceActorMsg pendingMsg = toDeviceMsg(msg); 55 + DeviceToDeviceActorMsg pendingMsg = toDeviceMsg(msg);
57 FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload(); 56 FromDeviceMsg fromDeviceMsg = pendingMsg.getPayload();
58 switch (fromDeviceMsg.getMsgType()) { 57 switch (fromDeviceMsg.getMsgType()) {
59 case POST_TELEMETRY_REQUEST: 58 case POST_TELEMETRY_REQUEST:
@@ -86,8 +85,8 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor { @@ -86,8 +85,8 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
86 @Override 85 @Override
87 public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) { 86 public void processToDeviceMsg(ActorContext context, ToDeviceMsg msg) {
88 try { 87 try {
89 - if (msg.getMsgType() != MsgType.SESSION_CLOSE) {  
90 - switch (msg.getMsgType()) { 88 + if (msg.getSessionMsgType() != SessionMsgType.SESSION_CLOSE) {
  89 + switch (msg.getSessionMsgType()) {
91 case STATUS_CODE_RESPONSE: 90 case STATUS_CODE_RESPONSE:
92 case GET_ATTRIBUTES_RESPONSE: 91 case GET_ATTRIBUTES_RESPONSE:
93 ResponseMsg responseMsg = (ResponseMsg) msg; 92 ResponseMsg responseMsg = (ResponseMsg) msg;
@@ -22,8 +22,8 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -22,8 +22,8 @@ import org.thingsboard.server.common.data.id.DeviceId;
22 import org.thingsboard.server.common.data.id.SessionId; 22 import org.thingsboard.server.common.data.id.SessionId;
23 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 23 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
24 import org.thingsboard.server.common.msg.cluster.ServerAddress; 24 import org.thingsboard.server.common.msg.cluster.ServerAddress;
25 -import org.thingsboard.server.common.msg.device.BasicToDeviceActorMsg;  
26 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 25 +import org.thingsboard.server.common.msg.device.BasicDeviceToDeviceActorMsg;
  26 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
27 import org.thingsboard.server.common.msg.session.*; 27 import org.thingsboard.server.common.msg.session.*;
28 import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; 28 import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
29 29
@@ -37,7 +37,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP @@ -37,7 +37,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
37 37
38 protected final SessionId sessionId; 38 protected final SessionId sessionId;
39 protected SessionContext sessionCtx; 39 protected SessionContext sessionCtx;
40 - protected ToDeviceActorMsg toDeviceActorMsgPrototype; 40 + protected DeviceToDeviceActorMsg deviceToDeviceActorMsgPrototype;
41 41
42 protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) { 42 protected AbstractSessionActorMsgProcessor(ActorSystemContext ctx, LoggingAdapter logger, SessionId sessionId) {
43 super(ctx, logger); 43 super(ctx, logger);
@@ -64,29 +64,29 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP @@ -64,29 +64,29 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
64 64
65 protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) { 65 protected void updateSessionCtx(ToDeviceActorSessionMsg msg, SessionType type) {
66 sessionCtx = msg.getSessionMsg().getSessionContext(); 66 sessionCtx = msg.getSessionMsg().getSessionContext();
67 - toDeviceActorMsgPrototype = new BasicToDeviceActorMsg(msg, type); 67 + deviceToDeviceActorMsgPrototype = new BasicDeviceToDeviceActorMsg(msg, type);
68 } 68 }
69 69
70 - protected ToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) { 70 + protected DeviceToDeviceActorMsg toDeviceMsg(ToDeviceActorSessionMsg msg) {
71 AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg(); 71 AdaptorToSessionActorMsg adaptorMsg = msg.getSessionMsg();
72 - return new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, adaptorMsg.getMsg()); 72 + return new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, adaptorMsg.getMsg());
73 } 73 }
74 74
75 - protected Optional<ToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) {  
76 - if (toDeviceActorMsgPrototype != null) {  
77 - return Optional.of(new BasicToDeviceActorMsg(toDeviceActorMsgPrototype, msg)); 75 + protected Optional<DeviceToDeviceActorMsg> toDeviceMsg(FromDeviceMsg msg) {
  76 + if (deviceToDeviceActorMsgPrototype != null) {
  77 + return Optional.of(new BasicDeviceToDeviceActorMsg(deviceToDeviceActorMsgPrototype, msg));
78 } else { 78 } else {
79 return Optional.empty(); 79 return Optional.empty();
80 } 80 }
81 } 81 }
82 82
83 - protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) { 83 + protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward) {
84 Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); 84 Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
85 forwardToAppActor(ctx, toForward, address); 85 forwardToAppActor(ctx, toForward, address);
86 return address; 86 return address;
87 } 87 }
88 88
89 - protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) { 89 + protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {
90 Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId()); 90 Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
91 if (!newAddress.equals(oldAddress)) { 91 if (!newAddress.equals(oldAddress)) {
92 if (newAddress.isPresent()) { 92 if (newAddress.isPresent()) {
@@ -99,7 +99,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP @@ -99,7 +99,7 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
99 return newAddress; 99 return newAddress;
100 } 100 }
101 101
102 - protected void forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> address) { 102 + protected void forwardToAppActor(ActorContext ctx, DeviceToDeviceActorMsg toForward, Optional<ServerAddress> address) {
103 if (address.isPresent()) { 103 if (address.isPresent()) {
104 systemContext.getRpcService().tell(address.get(), 104 systemContext.getRpcService().tell(address.get(),
105 toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer())); 105 toForward.toOtherAddress(systemContext.getRoutingService().getCurrentServer()));
@@ -114,6 +114,6 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP @@ -114,6 +114,6 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
114 } 114 }
115 115
116 public DeviceId getDeviceId() { 116 public DeviceId getDeviceId() {
117 - return toDeviceActorMsgPrototype.getDeviceId(); 117 + return deviceToDeviceActorMsgPrototype.getDeviceId();
118 } 118 }
119 } 119 }
@@ -20,7 +20,7 @@ import org.thingsboard.server.actors.shared.SessionTimeoutMsg; @@ -20,7 +20,7 @@ import org.thingsboard.server.actors.shared.SessionTimeoutMsg;
20 import org.thingsboard.server.common.data.id.SessionId; 20 import org.thingsboard.server.common.data.id.SessionId;
21 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 21 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
22 import org.thingsboard.server.common.msg.cluster.ServerAddress; 22 import org.thingsboard.server.common.msg.cluster.ServerAddress;
23 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 23 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
24 import org.thingsboard.server.common.msg.session.*; 24 import org.thingsboard.server.common.msg.session.*;
25 import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg; 25 import org.thingsboard.server.common.msg.session.ToDeviceActorSessionMsg;
26 import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; 26 import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg;
@@ -32,7 +32,7 @@ import akka.event.LoggingAdapter; @@ -32,7 +32,7 @@ import akka.event.LoggingAdapter;
32 import java.util.Optional; 32 import java.util.Optional;
33 33
34 class SyncMsgProcessor extends AbstractSessionActorMsgProcessor { 34 class SyncMsgProcessor extends AbstractSessionActorMsgProcessor {
35 - private ToDeviceActorMsg pendingMsg; 35 + private DeviceToDeviceActorMsg pendingMsg;
36 private Optional<ServerAddress> currentTargetServer; 36 private Optional<ServerAddress> currentTargetServer;
37 private boolean pendingResponse; 37 private boolean pendingResponse;
38 38
@@ -17,22 +17,32 @@ package org.thingsboard.server.actors.shared; @@ -17,22 +17,32 @@ package org.thingsboard.server.actors.shared;
17 17
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
19 import akka.event.LoggingAdapter; 19 import akka.event.LoggingAdapter;
  20 +import com.google.common.util.concurrent.FutureCallback;
  21 +import com.google.common.util.concurrent.Futures;
20 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
21 import org.thingsboard.server.actors.stats.StatsPersistTick; 23 import org.thingsboard.server.actors.stats.StatsPersistTick;
  24 +import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 26 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
  27 +import org.thingsboard.server.common.msg.TbMsg;
24 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 28 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  29 +import org.thingsboard.server.dao.queue.MsgQueue;
25 30
26 -public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgProcessor { 31 +import javax.annotation.Nullable;
  32 +import java.util.function.Consumer;
  33 +
  34 +public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor {
27 35
28 protected final TenantId tenantId; 36 protected final TenantId tenantId;
29 protected final T entityId; 37 protected final T entityId;
  38 + protected final MsgQueue queue;
30 protected ComponentLifecycleState state; 39 protected ComponentLifecycleState state;
31 40
32 protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) { 41 protected ComponentMsgProcessor(ActorSystemContext systemContext, LoggingAdapter logger, TenantId tenantId, T id) {
33 super(systemContext, logger); 42 super(systemContext, logger);
34 this.tenantId = tenantId; 43 this.tenantId = tenantId;
35 this.entityId = id; 44 this.entityId = id;
  45 + this.queue = systemContext.getMsgQueue();
36 } 46 }
37 47
38 public abstract void start(ActorContext context) throws Exception; 48 public abstract void start(ActorContext context) throws Exception;
@@ -75,4 +85,19 @@ public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgPr @@ -75,4 +85,19 @@ public abstract class ComponentMsgProcessor<T> extends AbstractContextAwareMsgPr
75 throw new IllegalStateException("Rule chain is not active!"); 85 throw new IllegalStateException("Rule chain is not active!");
76 } 86 }
77 } 87 }
  88 +
  89 + protected void putToQueue(final TbMsg tbMsg, final Consumer<TbMsg> onSuccess) {
  90 + EntityId entityId = tbMsg.getRuleNodeId() != null ? tbMsg.getRuleNodeId() : tbMsg.getRuleChainId();
  91 + Futures.addCallback(queue.put(tbMsg, entityId.getId(), 0), new FutureCallback<Void>() {
  92 + @Override
  93 + public void onSuccess(@Nullable Void result) {
  94 + onSuccess.accept(tbMsg);
  95 + }
  96 +
  97 + @Override
  98 + public void onFailure(Throwable t) {
  99 + logger.debug("Failed to push message [{}] to queue due to [{}]", tbMsg, t);
  100 + }
  101 + });
  102 + }
78 } 103 }
@@ -16,13 +16,16 @@ @@ -16,13 +16,16 @@
16 package org.thingsboard.server.actors.tenant; 16 package org.thingsboard.server.actors.tenant;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
  19 +import akka.actor.OneForOneStrategy;
19 import akka.actor.Props; 20 import akka.actor.Props;
20 -import akka.event.Logging;  
21 -import akka.event.LoggingAdapter; 21 +import akka.actor.SupervisorStrategy;
  22 +import akka.japi.Function;
22 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.actors.device.DeviceActor; 24 import org.thingsboard.server.actors.device.DeviceActor;
  25 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
24 import org.thingsboard.server.actors.plugin.PluginTerminationMsg; 26 import org.thingsboard.server.actors.plugin.PluginTerminationMsg;
25 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; 27 import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
  28 +import org.thingsboard.server.actors.ruleChain.RuleChainToRuleChainMsg;
26 import org.thingsboard.server.actors.service.ContextBasedCreator; 29 import org.thingsboard.server.actors.service.ContextBasedCreator;
27 import org.thingsboard.server.actors.service.DefaultActorService; 30 import org.thingsboard.server.actors.service.DefaultActorService;
28 import org.thingsboard.server.actors.shared.plugin.TenantPluginManager; 31 import org.thingsboard.server.actors.shared.plugin.TenantPluginManager;
@@ -30,11 +33,13 @@ import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager; @@ -30,11 +33,13 @@ import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager;
30 import org.thingsboard.server.common.data.id.DeviceId; 33 import org.thingsboard.server.common.data.id.DeviceId;
31 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.common.msg.TbActorMsg; 35 import org.thingsboard.server.common.msg.TbActorMsg;
33 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 36 +import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
  37 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
34 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
35 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 39 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
36 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 40 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
37 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; 41 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
  42 +import scala.concurrent.duration.Duration;
38 43
39 import java.util.HashMap; 44 import java.util.HashMap;
40 import java.util.Map; 45 import java.util.Map;
@@ -50,6 +55,12 @@ public class TenantActor extends RuleChainManagerActor { @@ -50,6 +55,12 @@ public class TenantActor extends RuleChainManagerActor {
50 this.deviceActors = new HashMap<>(); 55 this.deviceActors = new HashMap<>();
51 } 56 }
52 57
  58 +
  59 + @Override
  60 + public SupervisorStrategy supervisorStrategy() {
  61 + return strategy;
  62 + }
  63 +
53 @Override 64 @Override
54 public void preStart() { 65 public void preStart() {
55 logger.info("[{}] Starting tenant actor.", tenantId); 66 logger.info("[{}] Starting tenant actor.", tenantId);
@@ -64,63 +75,56 @@ public class TenantActor extends RuleChainManagerActor { @@ -64,63 +75,56 @@ public class TenantActor extends RuleChainManagerActor {
64 @Override 75 @Override
65 protected boolean process(TbActorMsg msg) { 76 protected boolean process(TbActorMsg msg) {
66 switch (msg.getMsgType()) { 77 switch (msg.getMsgType()) {
  78 + case CLUSTER_EVENT_MSG:
  79 + broadcast(msg);
  80 + break;
67 case COMPONENT_LIFE_CYCLE_MSG: 81 case COMPONENT_LIFE_CYCLE_MSG:
68 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 82 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
69 break; 83 break;
70 case SERVICE_TO_RULE_ENGINE_MSG: 84 case SERVICE_TO_RULE_ENGINE_MSG:
71 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg); 85 onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
72 break; 86 break;
  87 + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
  88 + onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
  89 + break;
  90 + case DEVICE_SESSION_TO_DEVICE_ACTOR_MSG:
  91 + case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
  92 + case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
  93 + case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  94 + case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  95 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  96 + onToDeviceActorMsg((DeviceAwareMsg) msg);
  97 + break;
  98 + case RULE_CHAIN_TO_RULE_CHAIN_MSG:
  99 + onRuleChainMsg((RuleChainToRuleChainMsg) msg);
  100 + break;
73 default: 101 default:
74 return false; 102 return false;
75 } 103 }
76 return true; 104 return true;
77 } 105 }
78 106
  107 + @Override
  108 + protected void broadcast(Object msg) {
  109 + super.broadcast(msg);
  110 + deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
  111 + }
  112 +
79 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { 113 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
80 ruleChainManager.getRootChainActor().tell(msg, self()); 114 ruleChainManager.getRootChainActor().tell(msg, self());
81 } 115 }
82 116
83 -  
84 -// @Override  
85 -// public void onReceive(Object msg) throws Exception {  
86 -// logger.debug("[{}] Received message: {}", tenantId, msg);  
87 -// if (msg instanceof ToDeviceActorMsg) {  
88 -// onToDeviceActorMsg((ToDeviceActorMsg) msg);  
89 -// } else if (msg instanceof ToPluginActorMsg) {  
90 -// onToPluginMsg((ToPluginActorMsg) msg);  
91 -// } else if (msg instanceof ToDeviceActorNotificationMsg) {  
92 -// onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);  
93 -// } else if (msg instanceof ClusterEventMsg) {  
94 -// broadcast(msg);  
95 -// } else if (msg instanceof ComponentLifecycleMsg) {  
96 -// onComponentLifecycleMsg((ComponentLifecycleMsg) msg);  
97 -// } else if (msg instanceof PluginTerminationMsg) {  
98 -// onPluginTerminated((PluginTerminationMsg) msg);  
99 -// } else {  
100 -// logger.warning("[{}] Unknown message: {}!", tenantId, msg);  
101 -// }  
102 -// }  
103 -  
104 - private void broadcast(Object msg) {  
105 - pluginManager.broadcast(msg);  
106 - deviceActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); 117 + private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
  118 + ruleChainManager.getRootChainActor().tell(msg, self());
107 } 119 }
108 120
109 - private void onToDeviceActorMsg(ToDeviceActorMsg msg) {  
110 - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender()); 121 + private void onRuleChainMsg(RuleChainToRuleChainMsg msg) {
  122 + ruleChainManager.getOrCreateActor(context(), msg.getTarget()).tell(msg, self());
111 } 123 }
112 124
113 - private void onToDeviceActorMsg(ToDeviceActorNotificationMsg msg) {  
114 - getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());  
115 - }  
116 125
117 - private void onToPluginMsg(ToPluginActorMsg msg) {  
118 - if (msg.getPluginTenantId().equals(tenantId)) {  
119 - ActorRef pluginActor = pluginManager.getOrCreateActor(this.context(), msg.getPluginId());  
120 - pluginActor.tell(msg, ActorRef.noSender());  
121 - } else {  
122 - context().parent().tell(msg, ActorRef.noSender());  
123 - } 126 + private void onToDeviceActorMsg(DeviceAwareMsg msg) {
  127 + getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
124 } 128 }
125 129
126 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 130 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
@@ -132,11 +136,6 @@ public class TenantActor extends RuleChainManagerActor { @@ -132,11 +136,6 @@ public class TenantActor extends RuleChainManagerActor {
132 } 136 }
133 } 137 }
134 138
135 - private void onPluginTerminated(PluginTerminationMsg msg) {  
136 - pluginManager.remove(msg.getId());  
137 - }  
138 -  
139 -  
140 private ActorRef getOrCreateDeviceActor(DeviceId deviceId) { 139 private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
141 return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId)) 140 return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId))
142 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString())); 141 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), deviceId.toString()));
@@ -158,4 +157,12 @@ public class TenantActor extends RuleChainManagerActor { @@ -158,4 +157,12 @@ public class TenantActor extends RuleChainManagerActor {
158 } 157 }
159 } 158 }
160 159
  160 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), new Function<Throwable, SupervisorStrategy.Directive>() {
  161 + @Override
  162 + public SupervisorStrategy.Directive apply(Throwable t) {
  163 + logger.error(t, "Unknown failure");
  164 + return SupervisorStrategy.resume();
  165 + }
  166 + });
  167 +
161 } 168 }
@@ -63,6 +63,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; @@ -63,6 +63,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
63 import org.thingsboard.server.common.data.exception.ThingsboardException; 63 import org.thingsboard.server.common.data.exception.ThingsboardException;
64 import org.thingsboard.server.service.component.ComponentDiscoveryService; 64 import org.thingsboard.server.service.component.ComponentDiscoveryService;
65 import org.thingsboard.server.service.security.model.SecurityUser; 65 import org.thingsboard.server.service.security.model.SecurityUser;
  66 +import org.thingsboard.server.service.state.DeviceStateService;
66 67
67 import javax.mail.MessagingException; 68 import javax.mail.MessagingException;
68 import javax.servlet.http.HttpServletRequest; 69 import javax.servlet.http.HttpServletRequest;
@@ -137,6 +138,9 @@ public abstract class BaseController { @@ -137,6 +138,9 @@ public abstract class BaseController {
137 @Autowired 138 @Autowired
138 protected DeviceOfflineService offlineService; 139 protected DeviceOfflineService offlineService;
139 140
  141 + @Autowired
  142 + protected DeviceStateService deviceStateService;
  143 +
140 @ExceptionHandler(ThingsboardException.class) 144 @ExceptionHandler(ThingsboardException.class)
141 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { 145 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
142 errorResponseHandler.handle(ex, response); 146 errorResponseHandler.handle(ex, response);
@@ -606,7 +610,7 @@ public abstract class BaseController { @@ -606,7 +610,7 @@ public abstract class BaseController {
606 auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); 610 auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo);
607 } 611 }
608 612
609 - protected static Exception toException(Throwable error) { 613 + public static Exception toException(Throwable error) {
610 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); 614 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
611 } 615 }
612 616
@@ -90,6 +90,11 @@ public class DeviceController extends BaseController { @@ -90,6 +90,11 @@ public class DeviceController extends BaseController {
90 savedDevice.getCustomerId(), 90 savedDevice.getCustomerId(),
91 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 91 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
92 92
  93 + if (device.getId() == null) {
  94 + deviceStateService.onDeviceAdded(savedDevice);
  95 + } else {
  96 + deviceStateService.onDeviceUpdated(savedDevice);
  97 + }
93 return savedDevice; 98 return savedDevice;
94 } catch (Exception e) { 99 } catch (Exception e) {
95 logEntityAction(emptyId(EntityType.DEVICE), device, 100 logEntityAction(emptyId(EntityType.DEVICE), device,
@@ -112,6 +117,7 @@ public class DeviceController extends BaseController { @@ -112,6 +117,7 @@ public class DeviceController extends BaseController {
112 device.getCustomerId(), 117 device.getCustomerId(),
113 ActionType.DELETED, null, strDeviceId); 118 ActionType.DELETED, null, strDeviceId);
114 119
  120 + deviceStateService.onDeviceDeleted(device);
115 } catch (Exception e) { 121 } catch (Exception e) {
116 logEntityAction(emptyId(EntityType.DEVICE), 122 logEntityAction(emptyId(EntityType.DEVICE),
117 null, 123 null,
@@ -387,7 +393,7 @@ public class DeviceController extends BaseController { @@ -387,7 +393,7 @@ public class DeviceController extends BaseController {
387 @RequestMapping(value = "/device/online", method = RequestMethod.GET) 393 @RequestMapping(value = "/device/online", method = RequestMethod.GET)
388 @ResponseBody 394 @ResponseBody
389 public List<Device> getOnlineDevices(@RequestParam("contactType") DeviceStatusQuery.ContactType contactType, 395 public List<Device> getOnlineDevices(@RequestParam("contactType") DeviceStatusQuery.ContactType contactType,
390 - @RequestParam("threshold") long threshold) throws ThingsboardException { 396 + @RequestParam("threshold") long threshold) throws ThingsboardException {
391 try { 397 try {
392 TenantId tenantId = getCurrentUser().getTenantId(); 398 TenantId tenantId = getCurrentUser().getTenantId();
393 ListenableFuture<List<Device>> offlineDevices = offlineService.findOnlineDevices(tenantId.getId(), contactType, threshold); 399 ListenableFuture<List<Device>> offlineDevices = offlineService.findOnlineDevices(tenantId.getId(), contactType, threshold);
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.google.common.util.concurrent.FutureCallback;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.http.HttpStatus;
  24 +import org.springframework.http.ResponseEntity;
  25 +import org.springframework.security.access.prepost.PreAuthorize;
  26 +import org.springframework.util.StringUtils;
  27 +import org.springframework.web.bind.annotation.PathVariable;
  28 +import org.springframework.web.bind.annotation.RequestBody;
  29 +import org.springframework.web.bind.annotation.RequestMapping;
  30 +import org.springframework.web.bind.annotation.RequestMethod;
  31 +import org.springframework.web.bind.annotation.ResponseBody;
  32 +import org.springframework.web.bind.annotation.RestController;
  33 +import org.springframework.web.context.request.async.DeferredResult;
  34 +import org.thingsboard.server.common.data.audit.ActionType;
  35 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  36 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  37 +import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.EntityId;
  39 +import org.thingsboard.server.common.data.id.TenantId;
  40 +import org.thingsboard.server.common.data.id.UUIDBased;
  41 +import org.thingsboard.server.common.data.rpc.RpcRequest;
  42 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  43 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  44 +import org.thingsboard.server.extensions.api.exception.ToErrorResponseEntity;
  45 +import org.thingsboard.server.extensions.api.plugins.PluginConstants;
  46 +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
  47 +import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
  48 +import org.thingsboard.server.service.rpc.DeviceRpcService;
  49 +import org.thingsboard.server.service.rpc.LocalRequestMetaData;
  50 +import org.thingsboard.server.service.security.AccessValidator;
  51 +import org.thingsboard.server.service.security.model.SecurityUser;
  52 +
  53 +import javax.annotation.Nullable;
  54 +import javax.annotation.PostConstruct;
  55 +import javax.annotation.PreDestroy;
  56 +import java.io.IOException;
  57 +import java.util.Optional;
  58 +import java.util.UUID;
  59 +import java.util.concurrent.ExecutorService;
  60 +import java.util.concurrent.Executors;
  61 +import java.util.function.Consumer;
  62 +
  63 +/**
  64 + * Created by ashvayka on 22.03.18.
  65 + */
  66 +@RestController
  67 +@RequestMapping(PluginConstants.RPC_URL_PREFIX)
  68 +@Slf4j
  69 +public class RpcController extends BaseController {
  70 +
  71 + public static final int DEFAULT_TIMEOUT = 10000;
  72 + protected final ObjectMapper jsonMapper = new ObjectMapper();
  73 +
  74 + @Autowired
  75 + private DeviceRpcService deviceRpcService;
  76 +
  77 + @Autowired
  78 + private AccessValidator accessValidator;
  79 +
  80 + private ExecutorService executor;
  81 +
  82 + @PostConstruct
  83 + public void initExecutor() {
  84 + executor = Executors.newSingleThreadExecutor();
  85 + }
  86 +
  87 + @PreDestroy
  88 + public void shutdownExecutor() {
  89 + if (executor != null) {
  90 + executor.shutdownNow();
  91 + }
  92 + }
  93 +
  94 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  95 + @RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
  96 + @ResponseBody
  97 + public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  98 + return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
  99 + }
  100 +
  101 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  102 + @RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
  103 + @ResponseBody
  104 + public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
  105 + return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
  106 + }
  107 +
  108 +
  109 + private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
  110 + try {
  111 + JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
  112 + RpcRequest cmd = new RpcRequest(rpcRequestBody.get("method").asText(),
  113 + jsonMapper.writeValueAsString(rpcRequestBody.get("params")));
  114 +
  115 + if (rpcRequestBody.has("timeout")) {
  116 + cmd.setTimeout(rpcRequestBody.get("timeout").asLong());
  117 + }
  118 + SecurityUser currentUser = getCurrentUser();
  119 + TenantId tenantId = currentUser.getTenantId();
  120 + final DeferredResult<ResponseEntity> response = new DeferredResult<>();
  121 + long timeout = System.currentTimeMillis() + (cmd.getTimeout() != null ? cmd.getTimeout() : DEFAULT_TIMEOUT);
  122 + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(cmd.getMethodName(), cmd.getRequestData());
  123 + accessValidator.validate(currentUser, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
  124 + @Override
  125 + public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
  126 + ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(),
  127 + tenantId,
  128 + deviceId,
  129 + oneWay,
  130 + timeout,
  131 + body
  132 + );
  133 + deviceRpcService.process(rpcRequest, new Consumer<FromDeviceRpcResponse>(){
  134 +
  135 + @Override
  136 + public void accept(FromDeviceRpcResponse fromDeviceRpcResponse) {
  137 + reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse);
  138 + }
  139 + });
  140 + }
  141 +
  142 + @Override
  143 + public void onFailure(Throwable e) {
  144 + ResponseEntity entity;
  145 + if (e instanceof ToErrorResponseEntity) {
  146 + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity();
  147 + } else {
  148 + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED);
  149 + }
  150 + logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e);
  151 + response.setResult(entity);
  152 + }
  153 + }));
  154 + return response;
  155 + } catch (IOException ioe) {
  156 + throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  157 + }
  158 + }
  159 +
  160 + public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) {
  161 + Optional<RpcError> rpcError = response.getError();
  162 + DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
  163 + if (rpcError.isPresent()) {
  164 + logRpcCall(rpcRequest, rpcError, null);
  165 + RpcError error = rpcError.get();
  166 + switch (error) {
  167 + case TIMEOUT:
  168 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
  169 + break;
  170 + case NO_ACTIVE_CONNECTION:
  171 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT));
  172 + break;
  173 + default:
  174 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
  175 + break;
  176 + }
  177 + } else {
  178 + Optional<String> responseData = response.getResponse();
  179 + if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) {
  180 + String data = responseData.get();
  181 + try {
  182 + logRpcCall(rpcRequest, rpcError, null);
  183 + responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK));
  184 + } catch (IOException e) {
  185 + log.debug("Failed to decode device response: {}", data, e);
  186 + logRpcCall(rpcRequest, rpcError, e);
  187 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));
  188 + }
  189 + } else {
  190 + logRpcCall(rpcRequest, rpcError, null);
  191 + responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
  192 + }
  193 + }
  194 + }
  195 +
  196 + private void logRpcCall(LocalRequestMetaData rpcRequest, Optional<RpcError> rpcError, Throwable e) {
  197 + logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null);
  198 + }
  199 +
  200 +
  201 + private void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e) {
  202 + String rpcErrorStr = "";
  203 + if (rpcError.isPresent()) {
  204 + rpcErrorStr = "RPC Error: " + rpcError.get().name();
  205 + }
  206 + String method = body.getMethod();
  207 + String params = body.getParams();
  208 +
  209 + auditLogService.logEntityAction(
  210 + user.getTenantId(),
  211 + user.getCustomerId(),
  212 + user.getId(),
  213 + user.getName(),
  214 + (UUIDBased & EntityId) entityId,
  215 + null,
  216 + ActionType.RPC_CALL,
  217 + BaseController.toException(e),
  218 + rpcErrorStr,
  219 + oneWay,
  220 + method,
  221 + params);
  222 + }
  223 +
  224 +
  225 +}
@@ -237,7 +237,7 @@ public class RuleChainController extends BaseController { @@ -237,7 +237,7 @@ public class RuleChainController extends BaseController {
237 ScriptEngine engine = null; 237 ScriptEngine engine = null;
238 try { 238 try {
239 engine = new NashornJsEngine(script, functionName, argNames); 239 engine = new NashornJsEngine(script, functionName, argNames);
240 - TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data); 240 + TbMsg inMsg = new TbMsg(UUIDs.timeBased(), msgType, null, new TbMsgMetaData(metadata), data, null, null, 0L);
241 switch (scriptType) { 241 switch (scriptType) {
242 case "update": 242 case "update":
243 output = msgToOutput(engine.executeUpdate(inMsg)); 243 output = msgToOutput(engine.executeUpdate(inMsg));
@@ -15,21 +15,34 @@ @@ -15,21 +15,34 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.http.HttpStatus; 20 import org.springframework.http.HttpStatus;
20 import org.springframework.security.access.prepost.PreAuthorize; 21 import org.springframework.security.access.prepost.PreAuthorize;
21 -import org.springframework.web.bind.annotation.*; 22 +import org.springframework.web.bind.annotation.PathVariable;
  23 +import org.springframework.web.bind.annotation.RequestBody;
  24 +import org.springframework.web.bind.annotation.RequestMapping;
  25 +import org.springframework.web.bind.annotation.RequestMethod;
  26 +import org.springframework.web.bind.annotation.RequestParam;
  27 +import org.springframework.web.bind.annotation.ResponseBody;
  28 +import org.springframework.web.bind.annotation.ResponseStatus;
  29 +import org.springframework.web.bind.annotation.RestController;
22 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.exception.ThingsboardException;
23 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.page.TextPageData; 33 import org.thingsboard.server.common.data.page.TextPageData;
25 import org.thingsboard.server.common.data.page.TextPageLink; 34 import org.thingsboard.server.common.data.page.TextPageLink;
26 import org.thingsboard.server.dao.tenant.TenantService; 35 import org.thingsboard.server.dao.tenant.TenantService;
27 -import org.thingsboard.server.common.data.exception.ThingsboardException; 36 +import org.thingsboard.server.service.install.InstallScripts;
28 37
29 @RestController 38 @RestController
30 @RequestMapping("/api") 39 @RequestMapping("/api")
  40 +@Slf4j
31 public class TenantController extends BaseController { 41 public class TenantController extends BaseController {
32 - 42 +
  43 + @Autowired
  44 + private InstallScripts installScripts;
  45 +
33 @Autowired 46 @Autowired
34 private TenantService tenantService; 47 private TenantService tenantService;
35 48
@@ -49,10 +62,15 @@ public class TenantController extends BaseController { @@ -49,10 +62,15 @@ public class TenantController extends BaseController {
49 62
50 @PreAuthorize("hasAuthority('SYS_ADMIN')") 63 @PreAuthorize("hasAuthority('SYS_ADMIN')")
51 @RequestMapping(value = "/tenant", method = RequestMethod.POST) 64 @RequestMapping(value = "/tenant", method = RequestMethod.POST)
52 - @ResponseBody 65 + @ResponseBody
53 public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException { 66 public Tenant saveTenant(@RequestBody Tenant tenant) throws ThingsboardException {
54 try { 67 try {
55 - return checkNotNull(tenantService.saveTenant(tenant)); 68 + boolean newTenant = tenant.getId() == null;
  69 + tenant = checkNotNull(tenantService.saveTenant(tenant));
  70 + if (newTenant) {
  71 + installScripts.createDefaultRuleChains(tenant.getId());
  72 + }
  73 + return tenant;
56 } catch (Exception e) { 74 } catch (Exception e) {
57 throw handleException(e); 75 throw handleException(e);
58 } 76 }
@@ -72,7 +90,7 @@ public class TenantController extends BaseController { @@ -72,7 +90,7 @@ public class TenantController extends BaseController {
72 } 90 }
73 91
74 @PreAuthorize("hasAuthority('SYS_ADMIN')") 92 @PreAuthorize("hasAuthority('SYS_ADMIN')")
75 - @RequestMapping(value = "/tenants", params = { "limit" }, method = RequestMethod.GET) 93 + @RequestMapping(value = "/tenants", params = {"limit"}, method = RequestMethod.GET)
76 @ResponseBody 94 @ResponseBody
77 public TextPageData<Tenant> getTenants(@RequestParam int limit, 95 public TextPageData<Tenant> getTenants(@RequestParam int limit,
78 @RequestParam(required = false) String textSearch, 96 @RequestParam(required = false) String textSearch,
@@ -85,5 +103,5 @@ public class TenantController extends BaseController { @@ -85,5 +103,5 @@ public class TenantController extends BaseController {
85 throw handleException(e); 103 throw handleException(e);
86 } 104 }
87 } 105 }
88 - 106 +
89 } 107 }
@@ -23,9 +23,7 @@ import org.springframework.context.ApplicationContext; @@ -23,9 +23,7 @@ import org.springframework.context.ApplicationContext;
23 import org.springframework.context.annotation.Profile; 23 import org.springframework.context.annotation.Profile;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
25 import org.thingsboard.server.service.component.ComponentDiscoveryService; 25 import org.thingsboard.server.service.component.ComponentDiscoveryService;
26 -import org.thingsboard.server.service.install.DatabaseSchemaService;  
27 -import org.thingsboard.server.service.install.DatabaseUpgradeService;  
28 -import org.thingsboard.server.service.install.SystemDataLoaderService; 26 +import org.thingsboard.server.service.install.*;
29 27
30 import java.nio.file.Paths; 28 import java.nio.file.Paths;
31 29
@@ -40,9 +38,6 @@ public class ThingsboardInstallService { @@ -40,9 +38,6 @@ public class ThingsboardInstallService {
40 @Value("${install.upgrade.from_version:1.2.3}") 38 @Value("${install.upgrade.from_version:1.2.3}")
41 private String upgradeFromVersion; 39 private String upgradeFromVersion;
42 40
43 - @Value("${install.data_dir}")  
44 - private String dataDir;  
45 -  
46 @Value("${install.load_demo:false}") 41 @Value("${install.load_demo:false}")
47 private Boolean loadDemo; 42 private Boolean loadDemo;
48 43
@@ -61,6 +56,9 @@ public class ThingsboardInstallService { @@ -61,6 +56,9 @@ public class ThingsboardInstallService {
61 @Autowired 56 @Autowired
62 private SystemDataLoaderService systemDataLoaderService; 57 private SystemDataLoaderService systemDataLoaderService;
63 58
  59 + @Autowired
  60 + private DataUpdateService dataUpdateService;
  61 +
64 public void performInstall() { 62 public void performInstall() {
65 try { 63 try {
66 if (isUpgrade) { 64 if (isUpgrade) {
@@ -87,6 +85,8 @@ public class ThingsboardInstallService { @@ -87,6 +85,8 @@ public class ThingsboardInstallService {
87 85
88 databaseUpgradeService.upgradeDatabase("1.4.0"); 86 databaseUpgradeService.upgradeDatabase("1.4.0");
89 87
  88 + dataUpdateService.updateData("1.4.0");
  89 +
90 log.info("Updating system data..."); 90 log.info("Updating system data...");
91 91
92 systemDataLoaderService.deleteSystemWidgetBundle("charts"); 92 systemDataLoaderService.deleteSystemWidgetBundle("charts");
@@ -113,13 +113,6 @@ public class ThingsboardInstallService { @@ -113,13 +113,6 @@ public class ThingsboardInstallService {
113 113
114 log.info("Starting ThingsBoard Installation..."); 114 log.info("Starting ThingsBoard Installation...");
115 115
116 - if (this.dataDir == null) {  
117 - throw new RuntimeException("'install.data_dir' property should specified!");  
118 - }  
119 - if (!Paths.get(this.dataDir).toFile().isDirectory()) {  
120 - throw new RuntimeException("'install.data_dir' property value is not a valid directory!");  
121 - }  
122 -  
123 log.info("Installing DataBase schema..."); 116 log.info("Installing DataBase schema...");
124 117
125 databaseSchemaService.createDatabaseSchema(); 118 databaseSchemaService.createDatabaseSchema();
@@ -131,8 +124,8 @@ public class ThingsboardInstallService { @@ -131,8 +124,8 @@ public class ThingsboardInstallService {
131 systemDataLoaderService.createSysAdmin(); 124 systemDataLoaderService.createSysAdmin();
132 systemDataLoaderService.createAdminSettings(); 125 systemDataLoaderService.createAdminSettings();
133 systemDataLoaderService.loadSystemWidgets(); 126 systemDataLoaderService.loadSystemWidgets();
134 - systemDataLoaderService.loadSystemPlugins();  
135 - systemDataLoaderService.loadSystemRules(); 127 +// systemDataLoaderService.loadSystemPlugins();
  128 +// systemDataLoaderService.loadSystemRules();
136 129
137 if (loadDemo) { 130 if (loadDemo) {
138 log.info("Loading demo data..."); 131 log.info("Loading demo data...");
@@ -19,7 +19,6 @@ import com.google.protobuf.ByteString; @@ -19,7 +19,6 @@ import com.google.protobuf.ByteString;
19 import io.grpc.Server; 19 import io.grpc.Server;
20 import io.grpc.ServerBuilder; 20 import io.grpc.ServerBuilder;
21 import io.grpc.stub.StreamObserver; 21 import io.grpc.stub.StreamObserver;
22 -import lombok.Setter;  
23 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
@@ -27,29 +26,24 @@ import org.springframework.util.SerializationUtils; @@ -27,29 +26,24 @@ import org.springframework.util.SerializationUtils;
27 import org.thingsboard.server.actors.rpc.RpcBroadcastMsg; 26 import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
28 import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; 27 import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
29 import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; 28 import org.thingsboard.server.actors.rpc.RpcSessionTellMsg;
30 -import org.thingsboard.server.actors.service.ActorService;  
31 import org.thingsboard.server.common.data.id.EntityId; 29 import org.thingsboard.server.common.data.id.EntityId;
32 import org.thingsboard.server.common.msg.cluster.ServerAddress; 30 import org.thingsboard.server.common.msg.cluster.ServerAddress;
33 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; 31 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
34 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; 32 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
35 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 33 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
36 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 34 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
37 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; 35 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
38 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;  
39 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg; 36 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
40 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; 37 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
41 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; 38 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
42 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 39 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
43 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc; 40 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
44 -import org.thingsboard.server.service.cluster.discovery.DiscoveryService;  
45 import org.thingsboard.server.service.cluster.discovery.ServerInstance; 41 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
46 import org.thingsboard.server.service.cluster.discovery.ServerInstanceService; 42 import org.thingsboard.server.service.cluster.discovery.ServerInstanceService;
47 -import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; 43 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
48 44
49 -import javax.annotation.PostConstruct;  
50 import javax.annotation.PreDestroy; 45 import javax.annotation.PreDestroy;
51 import java.io.IOException; 46 import java.io.IOException;
52 -import java.util.Set;  
53 import java.util.UUID; 47 import java.util.UUID;
54 import java.util.concurrent.ConcurrentHashMap; 48 import java.util.concurrent.ConcurrentHashMap;
55 import java.util.concurrent.ConcurrentMap; 49 import java.util.concurrent.ConcurrentMap;
@@ -124,7 +118,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @@ -124,7 +118,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
124 } 118 }
125 119
126 @Override 120 @Override
127 - public void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward) { 121 + public void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward) {
128 ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() 122 ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
129 .setToDeviceActorRpcMsg(toProtoMsg(toForward)).build(); 123 .setToDeviceActorRpcMsg(toProtoMsg(toForward)).build();
130 tell(serverAddress, msg); 124 tell(serverAddress, msg);
@@ -138,7 +132,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @@ -138,7 +132,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
138 } 132 }
139 133
140 @Override 134 @Override
141 - public void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward) { 135 + public void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward) {
142 ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder() 136 ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
143 .setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build(); 137 .setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build();
144 tell(serverAddress, msg); 138 tell(serverAddress, msg);
@@ -190,7 +184,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @@ -190,7 +184,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
190 } 184 }
191 } 185 }
192 186
193 - private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(ToDeviceActorMsg msg) { 187 + private static ClusterAPIProtos.ToDeviceActorRpcMessage toProtoMsg(DeviceToDeviceActorMsg msg) {
194 return ClusterAPIProtos.ToDeviceActorRpcMessage.newBuilder().setData( 188 return ClusterAPIProtos.ToDeviceActorRpcMessage.newBuilder().setData(
195 ByteString.copyFrom(SerializationUtils.serialize(msg)) 189 ByteString.copyFrom(SerializationUtils.serialize(msg))
196 ).build(); 190 ).build();
@@ -202,15 +196,10 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @@ -202,15 +196,10 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
202 ).build(); 196 ).build();
203 } 197 }
204 198
205 - private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestPluginMsg msg) { 199 + private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestActorMsg msg) {
206 ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder(); 200 ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder();
207 ToDeviceRpcRequest request = msg.getMsg(); 201 ToDeviceRpcRequest request = msg.getMsg();
208 202
209 - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder()  
210 - .setTenantId(toUid(msg.getPluginTenantId().getId()))  
211 - .setPluginId(toUid(msg.getPluginId().getId()))  
212 - .build());  
213 -  
214 builder.setDeviceTenantId(toUid(msg.getTenantId())); 203 builder.setDeviceTenantId(toUid(msg.getTenantId()));
215 builder.setDeviceId(toUid(msg.getDeviceId())); 204 builder.setDeviceId(toUid(msg.getDeviceId()));
216 205
@@ -227,11 +216,6 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI @@ -227,11 +216,6 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
227 ClusterAPIProtos.ToPluginRpcResponseRpcMessage.Builder builder = ClusterAPIProtos.ToPluginRpcResponseRpcMessage.newBuilder(); 216 ClusterAPIProtos.ToPluginRpcResponseRpcMessage.Builder builder = ClusterAPIProtos.ToPluginRpcResponseRpcMessage.newBuilder();
228 FromDeviceRpcResponse request = msg.getResponse(); 217 FromDeviceRpcResponse request = msg.getResponse();
229 218
230 - builder.setAddress(ClusterAPIProtos.PluginAddress.newBuilder()  
231 - .setTenantId(toUid(msg.getPluginTenantId().getId()))  
232 - .setPluginId(toUid(msg.getPluginId().getId()))  
233 - .build());  
234 -  
235 builder.setMsgId(toUid(request.getId())); 219 builder.setMsgId(toUid(request.getId()));
236 request.getResponse().ifPresent(builder::setResponse); 220 request.getResponse().ifPresent(builder::setResponse);
237 request.getError().ifPresent(e -> builder.setError(e.name())); 221 request.getError().ifPresent(e -> builder.setError(e.name()));
@@ -19,12 +19,12 @@ import io.grpc.stub.StreamObserver; @@ -19,12 +19,12 @@ import io.grpc.stub.StreamObserver;
19 import org.thingsboard.server.common.msg.cluster.ServerAddress; 19 import org.thingsboard.server.common.msg.cluster.ServerAddress;
20 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; 20 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
21 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; 21 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
22 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 22 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
23 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 23 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
24 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestPluginMsg;  
25 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg; 24 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
26 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg; 25 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
27 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 26 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
  27 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
28 28
29 import java.util.UUID; 29 import java.util.UUID;
30 30
@@ -35,13 +35,13 @@ public interface ClusterRpcService { @@ -35,13 +35,13 @@ public interface ClusterRpcService {
35 35
36 void init(RpcMsgListener listener); 36 void init(RpcMsgListener listener);
37 37
38 - void tell(ServerAddress serverAddress, ToDeviceActorMsg toForward); 38 + void tell(ServerAddress serverAddress, DeviceToDeviceActorMsg toForward);
39 39
40 void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward); 40 void tell(ServerAddress serverAddress, ToDeviceSessionActorMsg toForward);
41 41
42 void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward); 42 void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward);
43 43
44 - void tell(ServerAddress serverAddress, ToDeviceRpcRequestPluginMsg toForward); 44 + void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward);
45 45
46 void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward); 46 void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward);
47 47
@@ -50,4 +50,5 @@ public interface ClusterRpcService { @@ -50,4 +50,5 @@ public interface ClusterRpcService {
50 void broadcast(ToAllNodesMsg msg); 50 void broadcast(ToAllNodesMsg msg);
51 51
52 void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ToRpcServerMessage> inputStream); 52 void onSessionCreated(UUID msgUid, StreamObserver<ClusterAPIProtos.ToRpcServerMessage> inputStream);
  53 +
53 } 54 }
@@ -20,18 +20,16 @@ import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg; @@ -20,18 +20,16 @@ import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
20 import org.thingsboard.server.actors.rpc.RpcSessionTellMsg; 20 import org.thingsboard.server.actors.rpc.RpcSessionTellMsg;
21 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg; 21 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
22 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg; 22 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
23 -import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 23 +import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
24 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 24 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
25 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg; 25 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
26 -import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;  
27 -import org.thingsboard.server.gen.cluster.ClusterAPIProtos;  
28 26
29 /** 27 /**
30 * @author Andrew Shvayka 28 * @author Andrew Shvayka
31 */ 29 */
32 public interface RpcMsgListener { 30 public interface RpcMsgListener {
33 31
34 - void onMsg(ToDeviceActorMsg msg); 32 + void onMsg(DeviceToDeviceActorMsg msg);
35 33
36 void onMsg(ToDeviceActorNotificationMsg msg); 34 void onMsg(ToDeviceActorNotificationMsg msg);
37 35
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.executors;
  17 +
  18 +import org.springframework.beans.factory.annotation.Value;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +@Component
  22 +public class ExternalCallExecutorService extends AbstractListeningExecutor {
  23 +
  24 + @Value("${actors.rule.external_call_thread_pool_size}")
  25 + private int externalCallExecutorThreadPoolSize;
  26 +
  27 + @Override
  28 + protected int getThreadPollSize() {
  29 + return externalCallExecutorThreadPoolSize;
  30 + }
  31 +
  32 +}
  33 +
@@ -37,17 +37,16 @@ public class CassandraDatabaseSchemaService implements DatabaseSchemaService { @@ -37,17 +37,16 @@ public class CassandraDatabaseSchemaService implements DatabaseSchemaService {
37 private static final String CASSANDRA_DIR = "cassandra"; 37 private static final String CASSANDRA_DIR = "cassandra";
38 private static final String SCHEMA_CQL = "schema.cql"; 38 private static final String SCHEMA_CQL = "schema.cql";
39 39
40 - @Value("${install.data_dir}")  
41 - private String dataDir;  
42 -  
43 @Autowired 40 @Autowired
44 private CassandraInstallCluster cluster; 41 private CassandraInstallCluster cluster;
45 42
  43 + @Autowired
  44 + private InstallScripts installScripts;
  45 +
46 @Override 46 @Override
47 public void createDatabaseSchema() throws Exception { 47 public void createDatabaseSchema() throws Exception {
48 log.info("Installing Cassandra DataBase schema..."); 48 log.info("Installing Cassandra DataBase schema...");
49 -  
50 - Path schemaFile = Paths.get(this.dataDir, CASSANDRA_DIR, SCHEMA_CQL); 49 + Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, SCHEMA_CQL);
51 loadCql(schemaFile); 50 loadCql(schemaFile);
52 51
53 } 52 }
@@ -43,9 +43,6 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @@ -43,9 +43,6 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
43 43
44 private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; 44 private static final String SCHEMA_UPDATE_CQL = "schema_update.cql";
45 45
46 - @Value("${install.data_dir}")  
47 - private String dataDir;  
48 -  
49 @Autowired 46 @Autowired
50 private CassandraCluster cluster; 47 private CassandraCluster cluster;
51 48
@@ -55,6 +52,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @@ -55,6 +52,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
55 @Autowired 52 @Autowired
56 private DashboardService dashboardService; 53 private DashboardService dashboardService;
57 54
  55 + @Autowired
  56 + private InstallScripts installScripts;
  57 +
58 @Override 58 @Override
59 public void upgradeDatabase(String fromVersion) throws Exception { 59 public void upgradeDatabase(String fromVersion) throws Exception {
60 60
@@ -91,7 +91,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @@ -91,7 +91,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
91 log.info("Relations dumped."); 91 log.info("Relations dumped.");
92 92
93 log.info("Updating schema ..."); 93 log.info("Updating schema ...");
94 - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); 94 + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.0", SCHEMA_UPDATE_CQL);
95 loadCql(schemaUpdateFile); 95 loadCql(schemaUpdateFile);
96 log.info("Schema updated."); 96 log.info("Schema updated.");
97 97
@@ -173,7 +173,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @@ -173,7 +173,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
173 173
174 174
175 log.info("Updating schema ..."); 175 log.info("Updating schema ...");
176 - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); 176 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_CQL);
177 loadCql(schemaUpdateFile); 177 loadCql(schemaUpdateFile);
178 log.info("Schema updated."); 178 log.info("Schema updated.");
179 179
@@ -189,7 +189,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @@ -189,7 +189,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
189 case "1.4.0": 189 case "1.4.0":
190 190
191 log.info("Updating schema ..."); 191 log.info("Updating schema ...");
192 - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_CQL); 192 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.5.0", SCHEMA_UPDATE_CQL);
193 loadCql(schemaUpdateFile); 193 loadCql(schemaUpdateFile);
194 log.info("Schema updated."); 194 log.info("Schema updated.");
195 195
application/src/main/java/org/thingsboard/server/service/install/DataUpdateService.java renamed from application/src/test/java/org/thingsboard/server/controller/sql/RuleControllerSqlTest.java
@@ -13,14 +13,10 @@ @@ -13,14 +13,10 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.controller.sql; 16 +package org.thingsboard.server.service.install;
17 17
18 -import org.thingsboard.server.controller.BaseRuleControllerTest;  
19 -import org.thingsboard.server.dao.service.DaoSqlTest; 18 +public interface DataUpdateService {
  19 +
  20 + void updateData(String fromVersion) throws Exception;
20 21
21 -/**  
22 - * Created by Valerii Sosliuk on 6/28/2017.  
23 - */  
24 -@DaoSqlTest  
25 -public class RuleControllerSqlTest extends BaseRuleControllerTest {  
26 } 22 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.context.annotation.Profile;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.server.common.data.Tenant;
  23 +import org.thingsboard.server.common.data.id.IdBased;
  24 +import org.thingsboard.server.common.data.page.TextPageLink;
  25 +import org.thingsboard.server.common.data.rule.RuleChain;
  26 +import org.thingsboard.server.dao.rule.RuleChainService;
  27 +import org.thingsboard.server.dao.tenant.TenantService;
  28 +
  29 +import java.util.List;
  30 +import java.util.UUID;
  31 +
  32 +@Service
  33 +@Profile("install")
  34 +@Slf4j
  35 +public class DefaultDataUpdateService implements DataUpdateService {
  36 +
  37 + @Autowired
  38 + private TenantService tenantService;
  39 +
  40 + @Autowired
  41 + private RuleChainService ruleChainService;
  42 +
  43 + @Autowired
  44 + private InstallScripts installScripts;
  45 +
  46 + @Override
  47 + public void updateData(String fromVersion) throws Exception {
  48 + switch (fromVersion) {
  49 + case "1.4.0":
  50 + log.info("Updating data from version 1.4.0 to 1.5.0 ...");
  51 + tenantsDefaultRuleChainUpdater.updateEntities(null);
  52 + break;
  53 + default:
  54 + throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
  55 + }
  56 + }
  57 +
  58 + private PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater =
  59 + new PaginatedUpdater<String, Tenant>() {
  60 +
  61 + @Override
  62 + protected List<Tenant> findEntities(String region, TextPageLink pageLink) {
  63 + return tenantService.findTenants(pageLink).getData();
  64 + }
  65 +
  66 + @Override
  67 + protected void updateEntity(Tenant tenant) {
  68 + try {
  69 + RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId());
  70 + if (ruleChain == null) {
  71 + installScripts.createDefaultRuleChains(tenant.getId());
  72 + }
  73 + } catch (Exception e) {
  74 + log.error("Unable to update Tenant", e);
  75 + }
  76 + }
  77 + };
  78 +
  79 + public abstract class PaginatedUpdater<I, D extends IdBased<?>> {
  80 +
  81 + private static final int DEFAULT_LIMIT = 100;
  82 +
  83 + public void updateEntities(I id) {
  84 + TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
  85 + boolean hasNext = true;
  86 + while (hasNext) {
  87 + List<D> entities = findEntities(id, pageLink);
  88 + for (D entity : entities) {
  89 + updateEntity(entity);
  90 + }
  91 + hasNext = entities.size() == pageLink.getLimit();
  92 + if (hasNext) {
  93 + int index = entities.size() - 1;
  94 + UUID idOffset = entities.get(index).getUuidId();
  95 + pageLink.setIdOffset(idOffset);
  96 + }
  97 + }
  98 + }
  99 +
  100 + protected abstract List<D> findEntities(I id, TextPageLink pageLink);
  101 +
  102 + protected abstract void updateEntity(D entity);
  103 +
  104 + }
  105 +
  106 +}
@@ -60,21 +60,12 @@ import java.nio.file.Paths; @@ -60,21 +60,12 @@ import java.nio.file.Paths;
60 @Slf4j 60 @Slf4j
61 public class DefaultSystemDataLoaderService implements SystemDataLoaderService { 61 public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
62 62
63 - private static final String JSON_DIR = "json";  
64 - private static final String SYSTEM_DIR = "system";  
65 - private static final String DEMO_DIR = "demo";  
66 - private static final String WIDGET_BUNDLES_DIR = "widget_bundles";  
67 - private static final String PLUGINS_DIR = "plugins";  
68 - private static final String RULES_DIR = "rules";  
69 - private static final String DASHBOARDS_DIR = "dashboards";  
70 -  
71 private static final ObjectMapper objectMapper = new ObjectMapper(); 63 private static final ObjectMapper objectMapper = new ObjectMapper();
72 - public static final String JSON_EXT = ".json";  
73 public static final String CUSTOMER_CRED = "customer"; 64 public static final String CUSTOMER_CRED = "customer";
74 public static final String DEFAULT_DEVICE_TYPE = "default"; 65 public static final String DEFAULT_DEVICE_TYPE = "default";
75 66
76 - @Value("${install.data_dir}")  
77 - private String dataDir; 67 + @Autowired
  68 + private InstallScripts installScripts;
78 69
79 @Autowired 70 @Autowired
80 private BCryptPasswordEncoder passwordEncoder; 71 private BCryptPasswordEncoder passwordEncoder;
@@ -89,15 +80,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -89,15 +80,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
89 private WidgetsBundleService widgetsBundleService; 80 private WidgetsBundleService widgetsBundleService;
90 81
91 @Autowired 82 @Autowired
92 - private WidgetTypeService widgetTypeService;  
93 -  
94 - @Autowired  
95 - private PluginService pluginService;  
96 -  
97 - @Autowired  
98 - private RuleService ruleService;  
99 -  
100 - @Autowired  
101 private TenantService tenantService; 83 private TenantService tenantService;
102 84
103 @Autowired 85 @Autowired
@@ -109,9 +91,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -109,9 +91,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
109 @Autowired 91 @Autowired
110 private DeviceCredentialsService deviceCredentialsService; 92 private DeviceCredentialsService deviceCredentialsService;
111 93
112 - @Autowired  
113 - private DashboardService dashboardService;  
114 -  
115 @Bean 94 @Bean
116 protected BCryptPasswordEncoder passwordEncoder() { 95 protected BCryptPasswordEncoder passwordEncoder() {
117 return new BCryptPasswordEncoder(); 96 return new BCryptPasswordEncoder();
@@ -147,55 +126,12 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -147,55 +126,12 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
147 } 126 }
148 127
149 @Override 128 @Override
150 - public void loadSystemWidgets() throws Exception {  
151 - Path widgetBundlesDir = Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR);  
152 - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) {  
153 - dirStream.forEach(  
154 - path -> {  
155 - try {  
156 - JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile());  
157 - JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle");  
158 - WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class);  
159 - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);  
160 - JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes");  
161 - widgetTypesArrayJson.forEach(  
162 - widgetTypeJson -> {  
163 - try {  
164 - WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class);  
165 - widgetType.setBundleAlias(savedWidgetsBundle.getAlias());  
166 - widgetTypeService.saveWidgetType(widgetType);  
167 - } catch (Exception e) {  
168 - log.error("Unable to load widget type from json: [{}]", path.toString());  
169 - throw new RuntimeException("Unable to load widget type from json", e);  
170 - }  
171 - }  
172 - );  
173 - } catch (Exception e) {  
174 - log.error("Unable to load widgets bundle from json: [{}]", path.toString());  
175 - throw new RuntimeException("Unable to load widgets bundle from json", e);  
176 - }  
177 - }  
178 - );  
179 - }  
180 - }  
181 -  
182 - @Override  
183 - public void loadSystemPlugins() throws Exception {  
184 - loadPlugins(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, PLUGINS_DIR), null);  
185 - }  
186 -  
187 -  
188 - @Override  
189 - public void loadSystemRules() throws Exception {  
190 -// loadRules(Paths.get(dataDir, JSON_DIR, SYSTEM_DIR, RULES_DIR), null);  
191 - }  
192 -  
193 - @Override  
194 public void loadDemoData() throws Exception { 129 public void loadDemoData() throws Exception {
195 Tenant demoTenant = new Tenant(); 130 Tenant demoTenant = new Tenant();
196 demoTenant.setRegion("Global"); 131 demoTenant.setRegion("Global");
197 demoTenant.setTitle("Tenant"); 132 demoTenant.setTitle("Tenant");
198 demoTenant = tenantService.saveTenant(demoTenant); 133 demoTenant = tenantService.saveTenant(demoTenant);
  134 + installScripts.createDefaultRuleChains(demoTenant.getId());
199 createUser(Authority.TENANT_ADMIN, demoTenant.getId(), null, "tenant@thingsboard.org", "tenant"); 135 createUser(Authority.TENANT_ADMIN, demoTenant.getId(), null, "tenant@thingsboard.org", "tenant");
200 136
201 Customer customerA = new Customer(); 137 Customer customerA = new Customer();
@@ -227,9 +163,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -227,9 +163,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
227 createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + 163 createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
228 "Raspberry Pi GPIO control sample application"); 164 "Raspberry Pi GPIO control sample application");
229 165
230 - loadPlugins(Paths.get(dataDir, JSON_DIR, DEMO_DIR, PLUGINS_DIR), demoTenant.getId());  
231 -// loadRules(Paths.get(dataDir, JSON_DIR, DEMO_DIR, RULES_DIR), demoTenant.getId());  
232 - loadDashboards(Paths.get(dataDir, JSON_DIR, DEMO_DIR, DASHBOARDS_DIR), demoTenant.getId(), null); 166 + installScripts.loadDashboards(demoTenant.getId(), null);
233 } 167 }
234 168
235 @Override 169 @Override
@@ -240,6 +174,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -240,6 +174,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
240 } 174 }
241 } 175 }
242 176
  177 + @Override
  178 + public void loadSystemWidgets() throws Exception {
  179 + installScripts.loadSystemWidgets();
  180 + }
  181 +
243 private User createUser(Authority authority, 182 private User createUser(Authority authority,
244 TenantId tenantId, 183 TenantId tenantId,
245 CustomerId customerId, 184 CustomerId customerId,
@@ -282,72 +221,4 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -282,72 +221,4 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
282 return device; 221 return device;
283 } 222 }
284 223
285 - private void loadPlugins(Path pluginsDir, TenantId tenantId) throws Exception{  
286 - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(pluginsDir, path -> path.toString().endsWith(JSON_EXT))) {  
287 - dirStream.forEach(  
288 - path -> {  
289 - try {  
290 - JsonNode pluginJson = objectMapper.readTree(path.toFile());  
291 - PluginMetaData plugin = objectMapper.treeToValue(pluginJson, PluginMetaData.class);  
292 - plugin.setTenantId(tenantId);  
293 - if (plugin.getState() == ComponentLifecycleState.ACTIVE) {  
294 - plugin.setState(ComponentLifecycleState.SUSPENDED);  
295 - PluginMetaData savedPlugin = pluginService.savePlugin(plugin);  
296 - pluginService.activatePluginById(savedPlugin.getId());  
297 - } else {  
298 - pluginService.savePlugin(plugin);  
299 - }  
300 - } catch (Exception e) {  
301 - log.error("Unable to load plugin from json: [{}]", path.toString());  
302 - throw new RuntimeException("Unable to load plugin from json", e);  
303 - }  
304 - }  
305 - );  
306 - }  
307 - }  
308 -  
309 - private void loadRules(Path rulesDir, TenantId tenantId) throws Exception {  
310 - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(rulesDir, path -> path.toString().endsWith(JSON_EXT))) {  
311 - dirStream.forEach(  
312 - path -> {  
313 - try {  
314 - JsonNode ruleJson = objectMapper.readTree(path.toFile());  
315 - RuleMetaData rule = objectMapper.treeToValue(ruleJson, RuleMetaData.class);  
316 - rule.setTenantId(tenantId);  
317 - if (rule.getState() == ComponentLifecycleState.ACTIVE) {  
318 - rule.setState(ComponentLifecycleState.SUSPENDED);  
319 - RuleMetaData savedRule = ruleService.saveRule(rule);  
320 - ruleService.activateRuleById(savedRule.getId());  
321 - } else {  
322 - ruleService.saveRule(rule);  
323 - }  
324 - } catch (Exception e) {  
325 - log.error("Unable to load rule from json: [{}]", path.toString());  
326 - throw new RuntimeException("Unable to load rule from json", e);  
327 - }  
328 - }  
329 - );  
330 - }  
331 - }  
332 -  
333 - private void loadDashboards(Path dashboardsDir, TenantId tenantId, CustomerId customerId) throws Exception {  
334 - try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) {  
335 - dirStream.forEach(  
336 - path -> {  
337 - try {  
338 - JsonNode dashboardJson = objectMapper.readTree(path.toFile());  
339 - Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class);  
340 - dashboard.setTenantId(tenantId);  
341 - Dashboard savedDashboard = dashboardService.saveDashboard(dashboard);  
342 - if (customerId != null && !customerId.isNullUid()) {  
343 - dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId);  
344 - }  
345 - } catch (Exception e) {  
346 - log.error("Unable to load dashboard from json: [{}]", path.toString());  
347 - throw new RuntimeException("Unable to load dashboard from json", e);  
348 - }  
349 - }  
350 - );  
351 - }  
352 - }  
353 } 224 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Getter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.beans.factory.annotation.Value;
  23 +import org.springframework.stereotype.Component;
  24 +import org.springframework.util.StringUtils;
  25 +import org.thingsboard.server.common.data.Dashboard;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.rule.RuleChain;
  29 +import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  30 +import org.thingsboard.server.common.data.widget.WidgetType;
  31 +import org.thingsboard.server.common.data.widget.WidgetsBundle;
  32 +import org.thingsboard.server.dao.dashboard.DashboardService;
  33 +import org.thingsboard.server.dao.rule.RuleChainService;
  34 +import org.thingsboard.server.dao.widget.WidgetTypeService;
  35 +import org.thingsboard.server.dao.widget.WidgetsBundleService;
  36 +
  37 +import java.io.IOException;
  38 +import java.nio.file.DirectoryStream;
  39 +import java.nio.file.Files;
  40 +import java.nio.file.Path;
  41 +import java.nio.file.Paths;
  42 +
  43 +import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
  44 +
  45 +/**
  46 + * Created by ashvayka on 18.04.18.
  47 + */
  48 +@Component
  49 +@Slf4j
  50 +public class InstallScripts {
  51 +
  52 + public static final String APP_DIR = "application";
  53 + public static final String SRC_DIR = "src";
  54 + public static final String MAIN_DIR = "main";
  55 + public static final String DATA_DIR = "data";
  56 + public static final String JSON_DIR = "json";
  57 + public static final String SYSTEM_DIR = "system";
  58 + public static final String TENANT_DIR = "tenant";
  59 + public static final String DEMO_DIR = "demo";
  60 + public static final String RULE_CHAINS_DIR = "rule_chains";
  61 + public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
  62 + public static final String DASHBOARDS_DIR = "dashboards";
  63 +
  64 + public static final String JSON_EXT = ".json";
  65 +
  66 + @Value("${install.data_dir:}")
  67 + private String dataDir;
  68 +
  69 + @Autowired
  70 + private RuleChainService ruleChainService;
  71 +
  72 + @Autowired
  73 + private DashboardService dashboardService;
  74 +
  75 + @Autowired
  76 + private WidgetTypeService widgetTypeService;
  77 +
  78 + @Autowired
  79 + private WidgetsBundleService widgetsBundleService;
  80 +
  81 + public Path getTenantRuleChainsDir() {
  82 + return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
  83 + }
  84 +
  85 + public String getDataDir() {
  86 + if (!StringUtils.isEmpty(dataDir)) {
  87 + if (!Paths.get(this.dataDir).toFile().isDirectory()) {
  88 + throw new RuntimeException("'install.data_dir' property value is not a valid directory!");
  89 + }
  90 + return dataDir;
  91 + } else {
  92 + String workDir = System.getProperty("user.dir");
  93 + if (workDir.endsWith("application")) {
  94 + return Paths.get(workDir, SRC_DIR, MAIN_DIR, DATA_DIR).toString();
  95 + } else {
  96 + Path dataDirPath = Paths.get(workDir, APP_DIR, SRC_DIR, MAIN_DIR, DATA_DIR);
  97 + if (Files.exists(dataDirPath)) {
  98 + return dataDirPath.toString();
  99 + } else {
  100 + 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!");
  101 + }
  102 + }
  103 + }
  104 + }
  105 +
  106 + public void createDefaultRuleChains(TenantId tenantId) throws IOException {
  107 + Path tenantChainsDir = getTenantRuleChainsDir();
  108 + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(tenantChainsDir, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) {
  109 + dirStream.forEach(
  110 + path -> {
  111 + try {
  112 + JsonNode ruleChainJson = objectMapper.readTree(path.toFile());
  113 + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
  114 + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
  115 +
  116 + ruleChain.setTenantId(tenantId);
  117 + ruleChain = ruleChainService.saveRuleChain(ruleChain);
  118 +
  119 + ruleChainMetaData.setRuleChainId(ruleChain.getId());
  120 + ruleChainService.saveRuleChainMetaData(ruleChainMetaData);
  121 + } catch (Exception e) {
  122 + log.error("Unable to load rule chain from json: [{}]", path.toString());
  123 + throw new RuntimeException("Unable to load rule chain from json", e);
  124 + }
  125 + }
  126 + );
  127 + }
  128 + }
  129 +
  130 + public void loadSystemWidgets() throws Exception {
  131 + Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR);
  132 + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) {
  133 + dirStream.forEach(
  134 + path -> {
  135 + try {
  136 + JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile());
  137 + JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle");
  138 + WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class);
  139 + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
  140 + JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes");
  141 + widgetTypesArrayJson.forEach(
  142 + widgetTypeJson -> {
  143 + try {
  144 + WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class);
  145 + widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
  146 + widgetTypeService.saveWidgetType(widgetType);
  147 + } catch (Exception e) {
  148 + log.error("Unable to load widget type from json: [{}]", path.toString());
  149 + throw new RuntimeException("Unable to load widget type from json", e);
  150 + }
  151 + }
  152 + );
  153 + } catch (Exception e) {
  154 + log.error("Unable to load widgets bundle from json: [{}]", path.toString());
  155 + throw new RuntimeException("Unable to load widgets bundle from json", e);
  156 + }
  157 + }
  158 + );
  159 + }
  160 + }
  161 +
  162 + public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception {
  163 + Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR);
  164 + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) {
  165 + dirStream.forEach(
  166 + path -> {
  167 + try {
  168 + JsonNode dashboardJson = objectMapper.readTree(path.toFile());
  169 + Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class);
  170 + dashboard.setTenantId(tenantId);
  171 + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard);
  172 + if (customerId != null && !customerId.isNullUid()) {
  173 + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId);
  174 + }
  175 + } catch (Exception e) {
  176 + log.error("Unable to load dashboard from json: [{}]", path.toString());
  177 + throw new RuntimeException("Unable to load dashboard from json", e);
  178 + }
  179 + }
  180 + );
  181 + }
  182 + }
  183 +
  184 +
  185 +}
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.service.install; 16 package org.thingsboard.server.service.install;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.context.annotation.Profile; 21 import org.springframework.context.annotation.Profile;
21 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
@@ -38,9 +39,6 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { @@ -38,9 +39,6 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService {
38 private static final String SQL_DIR = "sql"; 39 private static final String SQL_DIR = "sql";
39 private static final String SCHEMA_SQL = "schema.sql"; 40 private static final String SCHEMA_SQL = "schema.sql";
40 41
41 - @Value("${install.data_dir}")  
42 - private String dataDir;  
43 -  
44 @Value("${spring.datasource.url}") 42 @Value("${spring.datasource.url}")
45 private String dbUrl; 43 private String dbUrl;
46 44
@@ -50,12 +48,15 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService { @@ -50,12 +48,15 @@ public class SqlDatabaseSchemaService implements DatabaseSchemaService {
50 @Value("${spring.datasource.password}") 48 @Value("${spring.datasource.password}")
51 private String dbPassword; 49 private String dbPassword;
52 50
  51 + @Autowired
  52 + private InstallScripts installScripts;
  53 +
53 @Override 54 @Override
54 public void createDatabaseSchema() throws Exception { 55 public void createDatabaseSchema() throws Exception {
55 56
56 log.info("Installing SQL DataBase schema..."); 57 log.info("Installing SQL DataBase schema...");
57 58
58 - Path schemaFile = Paths.get(this.dataDir, SQL_DIR, SCHEMA_SQL); 59 + Path schemaFile = Paths.get(installScripts.getDataDir(), SQL_DIR, SCHEMA_SQL);
59 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 60 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
60 String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8")); 61 String sql = new String(Files.readAllBytes(schemaFile), Charset.forName("UTF-8"));
61 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema 62 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema
@@ -44,9 +44,6 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -44,9 +44,6 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
44 44
45 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql"; 45 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql";
46 46
47 - @Value("${install.data_dir}")  
48 - private String dataDir;  
49 -  
50 @Value("${spring.datasource.url}") 47 @Value("${spring.datasource.url}")
51 private String dbUrl; 48 private String dbUrl;
52 49
@@ -59,12 +56,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -59,12 +56,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
59 @Autowired 56 @Autowired
60 private DashboardService dashboardService; 57 private DashboardService dashboardService;
61 58
  59 + @Autowired
  60 + private InstallScripts installScripts;
  61 +
62 @Override 62 @Override
63 public void upgradeDatabase(String fromVersion) throws Exception { 63 public void upgradeDatabase(String fromVersion) throws Exception {
64 switch (fromVersion) { 64 switch (fromVersion) {
65 case "1.3.0": 65 case "1.3.0":
66 log.info("Updating schema ..."); 66 log.info("Updating schema ...");
67 - Path schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); 67 + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.1", SCHEMA_UPDATE_SQL);
68 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 68 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
69 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); 69 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
70 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script 70 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
@@ -82,7 +82,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -82,7 +82,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
82 log.info("Dashboards dumped."); 82 log.info("Dashboards dumped.");
83 83
84 log.info("Updating schema ..."); 84 log.info("Updating schema ...");
85 - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); 85 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_SQL);
86 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); 86 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
87 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script 87 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
88 log.info("Schema updated."); 88 log.info("Schema updated.");
@@ -100,7 +100,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -100,7 +100,7 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
100 case "1.4.0": 100 case "1.4.0":
101 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 101 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
102 log.info("Updating schema ..."); 102 log.info("Updating schema ...");
103 - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_SQL); 103 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.5.0", SCHEMA_UPDATE_SQL);
104 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); 104 String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
105 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script 105 conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
106 log.info("Schema updated."); 106 log.info("Schema updated.");
@@ -23,10 +23,6 @@ public interface SystemDataLoaderService { @@ -23,10 +23,6 @@ public interface SystemDataLoaderService {
23 23
24 void loadSystemWidgets() throws Exception; 24 void loadSystemWidgets() throws Exception;
25 25
26 - void loadSystemPlugins() throws Exception;  
27 -  
28 - void loadSystemRules() throws Exception;  
29 -  
30 void loadDemoData() throws Exception; 26 void loadDemoData() throws Exception;
31 27
32 void deleteSystemWidgetBundle(String bundleAlias) throws Exception; 28 void deleteSystemWidgetBundle(String bundleAlias) throws Exception;
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.http.HttpStatus;
  22 +import org.springframework.http.ResponseEntity;
  23 +import org.springframework.stereotype.Service;
  24 +import org.springframework.util.StringUtils;
  25 +import org.springframework.web.context.request.async.DeferredResult;
  26 +import org.thingsboard.server.actors.service.ActorService;
  27 +import org.thingsboard.server.common.data.audit.ActionType;
  28 +import org.thingsboard.server.common.data.id.DeviceId;
  29 +import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.id.UUIDBased;
  32 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  33 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  34 +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
  35 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  36 +import org.thingsboard.server.controller.BaseController;
  37 +import org.thingsboard.server.dao.audit.AuditLogService;
  38 +import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
  39 +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
  40 +import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
  41 +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
  42 +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
  43 +import org.thingsboard.server.service.security.model.SecurityUser;
  44 +
  45 +import javax.annotation.PostConstruct;
  46 +import javax.annotation.PreDestroy;
  47 +import java.io.IOException;
  48 +import java.util.Optional;
  49 +import java.util.UUID;
  50 +import java.util.concurrent.ConcurrentHashMap;
  51 +import java.util.concurrent.ConcurrentMap;
  52 +import java.util.concurrent.Executors;
  53 +import java.util.concurrent.ScheduledExecutorService;
  54 +import java.util.concurrent.TimeUnit;
  55 +import java.util.function.BiConsumer;
  56 +import java.util.function.Consumer;
  57 +
  58 +/**
  59 + * Created by ashvayka on 27.03.18.
  60 + */
  61 +@Service
  62 +@Slf4j
  63 +public class DefaultDeviceRpcService implements DeviceRpcService {
  64 +
  65 + @Autowired
  66 + private ClusterRoutingService routingService;
  67 +
  68 + @Autowired
  69 + private ClusterRpcService rpcService;
  70 +
  71 + @Autowired
  72 + private ActorService actorService;
  73 +
  74 + @Autowired
  75 + private AuditLogService auditLogService;
  76 +
  77 + private ScheduledExecutorService rpcCallBackExecutor;
  78 +
  79 + private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localRpcRequests = new ConcurrentHashMap<>();
  80 +
  81 +
  82 + @PostConstruct
  83 + public void initExecutor() {
  84 + rpcCallBackExecutor = Executors.newSingleThreadScheduledExecutor();
  85 + }
  86 +
  87 + @PreDestroy
  88 + public void shutdownExecutor() {
  89 + if (rpcCallBackExecutor != null) {
  90 + rpcCallBackExecutor.shutdownNow();
  91 + }
  92 + }
  93 +
  94 + @Override
  95 + public void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) {
  96 + log.trace("[{}] Processing local rpc call for device [{}]", request.getTenantId(), request.getDeviceId());
  97 + sendRpcRequest(request);
  98 + UUID requestId = request.getId();
  99 + localRpcRequests.put(requestId, responseConsumer);
  100 + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis());
  101 + log.error("[{}] processing the request: [{}]", this.hashCode(), requestId);
  102 + rpcCallBackExecutor.schedule(() -> {
  103 + log.error("[{}] timeout the request: [{}]", this.hashCode(), requestId);
  104 + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId);
  105 + if (consumer != null) {
  106 + consumer.accept(new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT));
  107 + }
  108 + }, timeout, TimeUnit.MILLISECONDS);
  109 + }
  110 +
  111 + @Override
  112 + public void process(ToDeviceRpcRequest request, ServerAddress originator) {
  113 +// if (pluginServerAddress.isPresent()) {
  114 +// systemContext.getRpcService().tell(pluginServerAddress.get(), responsePluginMsg);
  115 +// logger.debug("[{}] Rpc command response sent to remote plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());
  116 +// } else {
  117 +// context.parent().tell(responsePluginMsg, ActorRef.noSender());
  118 +// logger.debug("[{}] Rpc command response sent to local plugin actor [{}]!", deviceId, requestMd.getMsg().getMsg().getId());
  119 +// }
  120 + }
  121 +
  122 + @Override
  123 + public void process(FromDeviceRpcResponse response) {
  124 + log.error("[{}] response the request: [{}]", this.hashCode(), response.getId());
  125 + //TODO: send to another server if needed.
  126 + UUID requestId = response.getId();
  127 + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId);
  128 + if (consumer != null) {
  129 + consumer.accept(response);
  130 + } else {
  131 + log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response);
  132 + }
  133 + }
  134 +
  135 + @Override
  136 + public void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body) {
  137 + ToServerRpcResponseActorMsg rpcMsg = new ToServerRpcResponseActorMsg(tenantId, deviceId, new ToServerRpcResponseMsg(requestId, body));
  138 + forward(deviceId, rpcMsg, rpcService::tell);
  139 + }
  140 +
  141 + private void sendRpcRequest(ToDeviceRpcRequest msg) {
  142 + log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg);
  143 + ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(msg);
  144 + forward(msg.getDeviceId(), rpcMsg, rpcService::tell);
  145 + }
  146 +
  147 + private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {
  148 + Optional<ServerAddress> instance = routingService.resolveById(deviceId);
  149 + if (instance.isPresent()) {
  150 + log.trace("[{}] Forwarding msg {} to remote device actor!", msg.getTenantId(), msg);
  151 + rpcFunction.accept(instance.get(), msg);
  152 + } else {
  153 + log.trace("[{}] Forwarding msg {} to local device actor!", msg.getTenantId(), msg);
  154 + actorService.onMsg(msg);
  155 + }
  156 + }
  157 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.rpc;
  17 +
  18 +import org.thingsboard.server.common.data.id.DeviceId;
  19 +import org.thingsboard.server.common.data.id.TenantId;
  20 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  21 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  22 +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
  23 +
  24 +import java.util.function.Consumer;
  25 +
  26 +/**
  27 + * Created by ashvayka on 16.04.18.
  28 + */
  29 +public interface DeviceRpcService {
  30 +
  31 + void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer);
  32 +
  33 + void process(ToDeviceRpcRequest request, ServerAddress originator);
  34 +
  35 + void process(FromDeviceRpcResponse response);
  36 +
  37 + void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body);
  38 +
  39 +}
application/src/main/java/org/thingsboard/server/service/rpc/LocalRequestMetaData.java renamed from extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/LocalRequestMetaData.java
@@ -13,18 +13,20 @@ @@ -13,18 +13,20 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.extensions.core.plugin.rpc; 16 +package org.thingsboard.server.service.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import org.springframework.http.ResponseEntity; 19 import org.springframework.http.ResponseEntity;
20 import org.springframework.web.context.request.async.DeferredResult; 20 import org.springframework.web.context.request.async.DeferredResult;
21 -import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest; 21 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  22 +import org.thingsboard.server.service.security.model.SecurityUser;
22 23
23 /** 24 /**
24 - * @author Andrew Shvayka 25 + * Created by ashvayka on 16.04.18.
25 */ 26 */
26 @Data 27 @Data
27 public class LocalRequestMetaData { 28 public class LocalRequestMetaData {
28 private final ToDeviceRpcRequest request; 29 private final ToDeviceRpcRequest request;
  30 + private final SecurityUser user;
29 private final DeferredResult<ResponseEntity> responseWriter; 31 private final DeferredResult<ResponseEntity> responseWriter;
30 } 32 }
application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java renamed from extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/msg/ToDeviceRpcRequestPluginMsg.java
@@ -13,36 +13,33 @@ @@ -13,36 +13,33 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.extensions.api.plugins.msg; 16 +package org.thingsboard.server.service.rpc;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 import lombok.RequiredArgsConstructor; 19 import lombok.RequiredArgsConstructor;
20 import lombok.ToString; 20 import lombok.ToString;
21 import org.thingsboard.server.common.data.id.DeviceId; 21 import org.thingsboard.server.common.data.id.DeviceId;
22 -import org.thingsboard.server.common.data.id.PluginId;  
23 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.msg.MsgType;
24 import org.thingsboard.server.common.msg.cluster.ServerAddress; 24 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  25 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
25 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg; 26 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
26 27
27 import java.util.Optional; 28 import java.util.Optional;
28 29
29 /** 30 /**
30 - * @author Andrew Shvayka 31 + * Created by ashvayka on 16.04.18.
31 */ 32 */
32 @ToString 33 @ToString
33 @RequiredArgsConstructor 34 @RequiredArgsConstructor
34 -public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg { 35 +public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg {
35 36
36 private final ServerAddress serverAddress; 37 private final ServerAddress serverAddress;
37 @Getter 38 @Getter
38 - private final PluginId pluginId;  
39 - @Getter  
40 - private final TenantId pluginTenantId;  
41 - @Getter  
42 private final ToDeviceRpcRequest msg; 39 private final ToDeviceRpcRequest msg;
43 40
44 - public ToDeviceRpcRequestPluginMsg(PluginId pluginId, TenantId pluginTenantId, ToDeviceRpcRequest msg) {  
45 - this(null, pluginId, pluginTenantId, msg); 41 + public ToDeviceRpcRequestActorMsg(ToDeviceRpcRequest msg) {
  42 + this(null, msg);
46 } 43 }
47 44
48 public Optional<ServerAddress> getServerAddress() { 45 public Optional<ServerAddress> getServerAddress() {
@@ -58,5 +55,9 @@ public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg @@ -58,5 +55,9 @@ public class ToDeviceRpcRequestPluginMsg implements ToDeviceActorNotificationMsg
58 public TenantId getTenantId() { 55 public TenantId getTenantId() {
59 return msg.getTenantId(); 56 return msg.getTenantId();
60 } 57 }
61 -}  
62 58
  59 + @Override
  60 + public MsgType getMsgType() {
  61 + return MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG;
  62 + }
  63 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.rpc;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.ToString;
  21 +import org.thingsboard.server.common.data.id.DeviceId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.msg.MsgType;
  24 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  25 +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
  26 +import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
  27 +
  28 +import java.util.Optional;
  29 +
  30 +/**
  31 + * Created by ashvayka on 16.04.18.
  32 + */
  33 +@ToString
  34 +@RequiredArgsConstructor
  35 +public class ToServerRpcResponseActorMsg implements ToDeviceActorNotificationMsg {
  36 +
  37 + private final ServerAddress serverAddress;
  38 +
  39 + @Getter
  40 + private final TenantId tenantId;
  41 +
  42 + @Getter
  43 + private final DeviceId deviceId;
  44 +
  45 + @Getter
  46 + private final ToServerRpcResponseMsg msg;
  47 +
  48 + public ToServerRpcResponseActorMsg(TenantId tenantId, DeviceId deviceId, ToServerRpcResponseMsg msg) {
  49 + this(null, tenantId, deviceId, msg);
  50 + }
  51 +
  52 + public Optional<ServerAddress> getServerAddress() {
  53 + return Optional.ofNullable(serverAddress);
  54 + }
  55 +
  56 + @Override
  57 + public MsgType getMsgType() {
  58 + return MsgType.SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG;
  59 + }
  60 +}
@@ -118,7 +118,7 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn @@ -118,7 +118,7 @@ public class NashornJsEngine implements org.thingsboard.rule.engine.api.ScriptEn
118 String newData = data != null ? data : msg.getData(); 118 String newData = data != null ? data : msg.getData();
119 TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData(); 119 TbMsgMetaData newMetadata = metadata != null ? new TbMsgMetaData(metadata) : msg.getMetaData();
120 String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType(); 120 String newMessageType = !StringUtils.isEmpty(messageType) ? messageType : msg.getType();
121 - return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData); 121 + return new TbMsg(msg.getId(), newMessageType, msg.getOriginator(), newMetadata, newData, msg.getRuleChainId(), msg.getRuleNodeId(), msg.getClusterPartition());
122 } catch (Throwable th) { 122 } catch (Throwable th) {
123 th.printStackTrace(); 123 th.printStackTrace();
124 throw new RuntimeException("Failed to unbind message data from javascript result", th); 124 throw new RuntimeException("Failed to unbind message data from javascript result", th);
@@ -25,6 +25,7 @@ import org.springframework.http.ResponseEntity; @@ -25,6 +25,7 @@ import org.springframework.http.ResponseEntity;
25 import org.springframework.stereotype.Component; 25 import org.springframework.stereotype.Component;
26 import org.springframework.web.context.request.async.DeferredResult; 26 import org.springframework.web.context.request.async.DeferredResult;
27 import org.thingsboard.server.actors.plugin.ValidationResult; 27 import org.thingsboard.server.actors.plugin.ValidationResult;
  28 +import org.thingsboard.server.common.data.BaseData;
28 import org.thingsboard.server.common.data.Customer; 29 import org.thingsboard.server.common.data.Customer;
29 import org.thingsboard.server.common.data.Device; 30 import org.thingsboard.server.common.data.Device;
30 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
@@ -35,8 +36,10 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -35,8 +36,10 @@ import org.thingsboard.server.common.data.id.DeviceId;
35 import org.thingsboard.server.common.data.id.EntityId; 36 import org.thingsboard.server.common.data.id.EntityId;
36 import org.thingsboard.server.common.data.id.EntityIdFactory; 37 import org.thingsboard.server.common.data.id.EntityIdFactory;
37 import org.thingsboard.server.common.data.id.RuleChainId; 38 import org.thingsboard.server.common.data.id.RuleChainId;
  39 +import org.thingsboard.server.common.data.id.RuleNodeId;
38 import org.thingsboard.server.common.data.id.TenantId; 40 import org.thingsboard.server.common.data.id.TenantId;
39 import org.thingsboard.server.common.data.rule.RuleChain; 41 import org.thingsboard.server.common.data.rule.RuleChain;
  42 +import org.thingsboard.server.common.data.rule.RuleNode;
40 import org.thingsboard.server.controller.HttpValidationCallback; 43 import org.thingsboard.server.controller.HttpValidationCallback;
41 import org.thingsboard.server.dao.alarm.AlarmService; 44 import org.thingsboard.server.dao.alarm.AlarmService;
42 import org.thingsboard.server.dao.asset.AssetService; 45 import org.thingsboard.server.dao.asset.AssetService;
@@ -140,7 +143,7 @@ public class AccessValidator { @@ -140,7 +143,7 @@ public class AccessValidator {
140 return response; 143 return response;
141 } 144 }
142 145
143 - public <T> void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { 146 + public void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
144 switch (entityId.getEntityType()) { 147 switch (entityId.getEntityType()) {
145 case DEVICE: 148 case DEVICE:
146 validateDevice(currentUser, entityId, callback); 149 validateDevice(currentUser, entityId, callback);
@@ -177,14 +180,14 @@ public class AccessValidator { @@ -177,14 +180,14 @@ public class AccessValidator {
177 } else if (currentUser.isCustomerUser() && !device.getCustomerId().equals(currentUser.getCustomerId())) { 180 } else if (currentUser.isCustomerUser() && !device.getCustomerId().equals(currentUser.getCustomerId())) {
178 return ValidationResult.accessDenied("Device doesn't belong to the current Customer!"); 181 return ValidationResult.accessDenied("Device doesn't belong to the current Customer!");
179 } else { 182 } else {
180 - return ValidationResult.ok(); 183 + return ValidationResult.ok(device);
181 } 184 }
182 } 185 }
183 }), executor); 186 }), executor);
184 } 187 }
185 } 188 }
186 189
187 - private <T> void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { 190 + private void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
188 if (currentUser.isSystemAdmin()) { 191 if (currentUser.isSystemAdmin()) {
189 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 192 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
190 } else { 193 } else {
@@ -198,15 +201,14 @@ public class AccessValidator { @@ -198,15 +201,14 @@ public class AccessValidator {
198 } else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) { 201 } else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) {
199 return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!"); 202 return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");
200 } else { 203 } else {
201 - return ValidationResult.ok(); 204 + return ValidationResult.ok(asset);
202 } 205 }
203 } 206 }
204 }), executor); 207 }), executor);
205 } 208 }
206 } 209 }
207 210
208 -  
209 - private <T> void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { 211 + private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
210 if (currentUser.isCustomerUser()) { 212 if (currentUser.isCustomerUser()) {
211 callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 213 callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
212 } else { 214 } else {
@@ -220,14 +222,40 @@ public class AccessValidator { @@ -220,14 +222,40 @@ public class AccessValidator {
220 } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { 222 } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
221 return ValidationResult.accessDenied("Rule chain is not in system scope!"); 223 return ValidationResult.accessDenied("Rule chain is not in system scope!");
222 } else { 224 } else {
223 - return ValidationResult.ok(); 225 + return ValidationResult.ok(ruleChain);
  226 + }
  227 + }
  228 + }), executor);
  229 + }
  230 + }
  231 +
  232 + private void validateRule(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
  233 + if (currentUser.isCustomerUser()) {
  234 + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  235 + } else {
  236 + ListenableFuture<RuleNode> ruleNodeFuture = ruleChainService.findRuleNodeByIdAsync(new RuleNodeId(entityId.getId()));
  237 + Futures.addCallback(ruleNodeFuture, getCallback(callback, ruleNodeTmp -> {
  238 + RuleNode ruleNode = ruleNodeTmp;
  239 + if (ruleNode == null) {
  240 + return ValidationResult.entityNotFound("Rule node with requested id wasn't found!");
  241 + } else if (ruleNode.getRuleChainId() == null) {
  242 + return ValidationResult.entityNotFound("Rule chain with requested node id wasn't found!");
  243 + } else {
  244 + //TODO: make async
  245 + RuleChain ruleChain = ruleChainService.findRuleChainById(ruleNode.getRuleChainId());
  246 + if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) {
  247 + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!");
  248 + } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
  249 + return ValidationResult.accessDenied("Rule chain is not in system scope!");
  250 + } else {
  251 + return ValidationResult.ok(ruleNode);
224 } 252 }
225 } 253 }
226 }), executor); 254 }), executor);
227 } 255 }
228 } 256 }
229 257
230 - private <T> void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { 258 + private void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
231 if (currentUser.isSystemAdmin()) { 259 if (currentUser.isSystemAdmin()) {
232 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 260 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
233 } else { 261 } else {
@@ -241,18 +269,18 @@ public class AccessValidator { @@ -241,18 +269,18 @@ public class AccessValidator {
241 } else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) { 269 } else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) {
242 return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!"); 270 return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");
243 } else { 271 } else {
244 - return ValidationResult.ok(); 272 + return ValidationResult.ok(customer);
245 } 273 }
246 } 274 }
247 }), executor); 275 }), executor);
248 } 276 }
249 } 277 }
250 278
251 - private <T> void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { 279 + private void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
252 if (currentUser.isCustomerUser()) { 280 if (currentUser.isCustomerUser()) {
253 callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 281 callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
254 } else if (currentUser.isSystemAdmin()) { 282 } else if (currentUser.isSystemAdmin()) {
255 - callback.onSuccess(ValidationResult.ok()); 283 + callback.onSuccess(ValidationResult.ok(null));
256 } else { 284 } else {
257 ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); 285 ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
258 Futures.addCallback(tenantFuture, getCallback(callback, tenant -> { 286 Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
@@ -261,13 +289,13 @@ public class AccessValidator { @@ -261,13 +289,13 @@ public class AccessValidator {
261 } else if (!tenant.getId().equals(currentUser.getTenantId())) { 289 } else if (!tenant.getId().equals(currentUser.getTenantId())) {
262 return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!"); 290 return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
263 } else { 291 } else {
264 - return ValidationResult.ok(); 292 + return ValidationResult.ok(tenant);
265 } 293 }
266 }), executor); 294 }), executor);
267 } 295 }
268 } 296 }
269 297
270 - private <T> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult> transformer) { 298 + private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) {
271 return new FutureCallback<T>() { 299 return new FutureCallback<T>() {
272 @Override 300 @Override
273 public void onSuccess(@Nullable T result) { 301 public void onSuccess(@Nullable T result) {
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.state;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.google.common.base.Function;
  21 +import com.google.common.util.concurrent.FutureCallback;
  22 +import com.google.common.util.concurrent.Futures;
  23 +import com.google.common.util.concurrent.ListenableFuture;
  24 +import com.google.common.util.concurrent.ListeningScheduledExecutorService;
  25 +import com.google.common.util.concurrent.MoreExecutors;
  26 +import lombok.Getter;
  27 +import lombok.extern.slf4j.Slf4j;
  28 +import org.springframework.beans.factory.annotation.Autowired;
  29 +import org.springframework.beans.factory.annotation.Value;
  30 +import org.springframework.stereotype.Service;
  31 +import org.thingsboard.server.actors.service.ActorService;
  32 +import org.thingsboard.server.common.data.DataConstants;
  33 +import org.thingsboard.server.common.data.Device;
  34 +import org.thingsboard.server.common.data.Tenant;
  35 +import org.thingsboard.server.common.data.id.DeviceId;
  36 +import org.thingsboard.server.common.data.id.TenantId;
  37 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  38 +import org.thingsboard.server.common.data.page.TextPageLink;
  39 +import org.thingsboard.server.common.msg.TbMsg;
  40 +import org.thingsboard.server.common.msg.TbMsgDataType;
  41 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  42 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
  43 +import org.thingsboard.server.dao.attributes.AttributesService;
  44 +import org.thingsboard.server.dao.device.DeviceService;
  45 +import org.thingsboard.server.dao.tenant.TenantService;
  46 +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  47 +
  48 +import javax.annotation.Nullable;
  49 +import javax.annotation.PostConstruct;
  50 +import javax.annotation.PreDestroy;
  51 +import java.util.ArrayList;
  52 +import java.util.Arrays;
  53 +import java.util.HashSet;
  54 +import java.util.List;
  55 +import java.util.Optional;
  56 +import java.util.Set;
  57 +import java.util.concurrent.ConcurrentHashMap;
  58 +import java.util.concurrent.ConcurrentMap;
  59 +import java.util.concurrent.ExecutionException;
  60 +import java.util.concurrent.Executors;
  61 +import java.util.concurrent.TimeUnit;
  62 +
  63 +import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT;
  64 +import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT;
  65 +import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT;
  66 +import static org.thingsboard.server.common.data.DataConstants.INACTIVITY_EVENT;
  67 +
  68 +/**
  69 + * Created by ashvayka on 01.05.18.
  70 + */
  71 +@Service
  72 +@Slf4j
  73 +//TODO: refactor to use page links as cursor and not fetch all
  74 +public class DefaultDeviceStateService implements DeviceStateService {
  75 +
  76 + private static final ObjectMapper json = new ObjectMapper();
  77 + public static final String ACTIVITY_STATE = "active";
  78 + public static final String LAST_CONNECT_TIME = "lastConnectTime";
  79 + public static final String LAST_DISCONNECT_TIME = "lastDisconnectTime";
  80 + public static final String LAST_ACTIVITY_TIME = "lastActivityTime";
  81 + public static final String INACTIVITY_ALARM_TIME = "inactivityAlarmTime";
  82 + public static final String INACTIVITY_TIMEOUT = "inactivityTimeout";
  83 +
  84 + public static final List<String> PERSISTENT_ATTRIBUTES = Arrays.asList(ACTIVITY_STATE, LAST_CONNECT_TIME, LAST_DISCONNECT_TIME, LAST_ACTIVITY_TIME, INACTIVITY_ALARM_TIME, INACTIVITY_TIMEOUT);
  85 +
  86 + @Autowired
  87 + private TenantService tenantService;
  88 +
  89 + @Autowired
  90 + private DeviceService deviceService;
  91 +
  92 + @Autowired
  93 + private AttributesService attributesService;
  94 +
  95 + @Autowired
  96 + private ActorService actorService;
  97 +
  98 + @Autowired
  99 + private TelemetrySubscriptionService tsSubService;
  100 +
  101 + @Value("${state.defaultInactivityTimeoutInSec}")
  102 + @Getter
  103 + private long defaultInactivityTimeoutInSec;
  104 +
  105 + @Value("${state.defaultStateCheckIntervalInSec}")
  106 + @Getter
  107 + private long defaultStateCheckIntervalInSec;
  108 +
  109 +// TODO in v2.1
  110 +// @Value("${state.defaultStatePersistenceIntervalInSec}")
  111 +// @Getter
  112 +// private long defaultStatePersistenceIntervalInSec;
  113 +//
  114 +// @Value("${state.defaultStatePersistencePack}")
  115 +// @Getter
  116 +// private long defaultStatePersistencePack;
  117 +
  118 + private ListeningScheduledExecutorService queueExecutor;
  119 +
  120 + private ConcurrentMap<TenantId, Set<DeviceId>> tenantDevices = new ConcurrentHashMap<>();
  121 + private ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
  122 +
  123 + @PostConstruct
  124 + public void init() {
  125 + // Should be always single threaded due to absence of locks.
  126 + queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
  127 + queueExecutor.submit(this::initStateFromDB);
  128 + queueExecutor.scheduleAtFixedRate(this::updateState, defaultStateCheckIntervalInSec, defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
  129 + //TODO: schedule persistence in v2.1;
  130 + }
  131 +
  132 + @PreDestroy
  133 + public void stop() {
  134 + if (queueExecutor != null) {
  135 + queueExecutor.shutdownNow();
  136 + }
  137 + }
  138 +
  139 + @Override
  140 + public void onDeviceAdded(Device device) {
  141 + queueExecutor.submit(() -> onDeviceAddedSync(device));
  142 + }
  143 +
  144 + @Override
  145 + public void onDeviceUpdated(Device device) {
  146 + queueExecutor.submit(() -> onDeviceUpdatedSync(device));
  147 + }
  148 +
  149 + @Override
  150 + public void onDeviceConnect(DeviceId deviceId) {
  151 + queueExecutor.submit(() -> onDeviceConnectSync(deviceId));
  152 + }
  153 +
  154 + @Override
  155 + public void onDeviceActivity(DeviceId deviceId) {
  156 + queueExecutor.submit(() -> onDeviceActivitySync(deviceId));
  157 + }
  158 +
  159 + @Override
  160 + public void onDeviceDisconnect(DeviceId deviceId) {
  161 + queueExecutor.submit(() -> onDeviceDisconnectSync(deviceId));
  162 + }
  163 +
  164 + @Override
  165 + public void onDeviceDeleted(Device device) {
  166 + queueExecutor.submit(() -> onDeviceDeleted(device.getTenantId(), device.getId()));
  167 + }
  168 +
  169 + @Override
  170 + public void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) {
  171 + queueExecutor.submit(() -> onInactivityTimeoutUpdate(deviceId, inactivityTimeout));
  172 + }
  173 +
  174 + @Override
  175 + public Optional<DeviceState> getDeviceState(DeviceId deviceId) {
  176 + DeviceStateData state = deviceStates.get(deviceId);
  177 + if (state != null) {
  178 + return Optional.of(state.getState());
  179 + } else {
  180 + return Optional.empty();
  181 + }
  182 + }
  183 +
  184 + private void initStateFromDB() {
  185 + List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData();
  186 + for (Tenant tenant : tenants) {
  187 + List<ListenableFuture<DeviceStateData>> fetchFutures = new ArrayList<>();
  188 + List<Device> devices = deviceService.findDevicesByTenantId(tenant.getId(), new TextPageLink(Integer.MAX_VALUE)).getData();
  189 + for (Device device : devices) {
  190 + fetchFutures.add(fetchDeviceState(device));
  191 + }
  192 + try {
  193 + Futures.successfulAsList(fetchFutures).get().forEach(this::addDeviceUsingState);
  194 + } catch (InterruptedException | ExecutionException e) {
  195 + log.warn("Failed to init device state service from DB", e);
  196 + }
  197 + }
  198 + }
  199 +
  200 + private void addDeviceUsingState(DeviceStateData state) {
  201 + tenantDevices.computeIfAbsent(state.getTenantId(), id -> ConcurrentHashMap.newKeySet()).add(state.getDeviceId());
  202 + deviceStates.put(state.getDeviceId(), state);
  203 + }
  204 +
  205 + private void updateState() {
  206 + long ts = System.currentTimeMillis();
  207 + Set<DeviceId> deviceIds = new HashSet<>(deviceStates.keySet());
  208 + for (DeviceId deviceId : deviceIds) {
  209 + DeviceStateData stateData = deviceStates.get(deviceId);
  210 + DeviceState state = stateData.getState();
  211 + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout());
  212 + if (!state.isActive() && state.getLastInactivityAlarmTime() < state.getLastActivityTime()) {
  213 + state.setLastInactivityAlarmTime(ts);
  214 + pushRuleEngineMessage(stateData, INACTIVITY_EVENT);
  215 + saveAttribute(deviceId, INACTIVITY_ALARM_TIME, ts);
  216 + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive());
  217 + }
  218 + }
  219 + }
  220 +
  221 + private void onDeviceConnectSync(DeviceId deviceId) {
  222 + DeviceStateData stateData = deviceStates.get(deviceId);
  223 + if (stateData != null) {
  224 + long ts = System.currentTimeMillis();
  225 + stateData.getState().setLastConnectTime(ts);
  226 + pushRuleEngineMessage(stateData, CONNECT_EVENT);
  227 + saveAttribute(deviceId, LAST_CONNECT_TIME, ts);
  228 + }
  229 + }
  230 +
  231 + private void onDeviceDisconnectSync(DeviceId deviceId) {
  232 + DeviceStateData stateData = deviceStates.get(deviceId);
  233 + if (stateData != null) {
  234 + long ts = System.currentTimeMillis();
  235 + stateData.getState().setLastDisconnectTime(ts);
  236 + pushRuleEngineMessage(stateData, DISCONNECT_EVENT);
  237 + saveAttribute(deviceId, LAST_DISCONNECT_TIME, ts);
  238 + }
  239 + }
  240 +
  241 + private void onDeviceActivitySync(DeviceId deviceId) {
  242 + DeviceStateData stateData = deviceStates.get(deviceId);
  243 + if (stateData != null) {
  244 + DeviceState state = stateData.getState();
  245 + long ts = System.currentTimeMillis();
  246 + state.setActive(true);
  247 + stateData.getState().setLastActivityTime(ts);
  248 + pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
  249 + saveAttribute(deviceId, LAST_ACTIVITY_TIME, ts);
  250 + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive());
  251 + }
  252 + }
  253 +
  254 + private void onInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) {
  255 + if (inactivityTimeout == 0L) {
  256 + return;
  257 + }
  258 + DeviceStateData stateData = deviceStates.get(deviceId);
  259 + if (stateData != null) {
  260 + long ts = System.currentTimeMillis();
  261 + DeviceState state = stateData.getState();
  262 + state.setInactivityTimeout(inactivityTimeout);
  263 + boolean oldActive = state.isActive();
  264 + state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout());
  265 + if (!oldActive && state.isActive()) {
  266 + saveAttribute(deviceId, ACTIVITY_STATE, state.isActive());
  267 + }
  268 + }
  269 + }
  270 +
  271 + private void onDeviceAddedSync(Device device) {
  272 + Futures.addCallback(fetchDeviceState(device), new FutureCallback<DeviceStateData>() {
  273 + @Override
  274 + public void onSuccess(@Nullable DeviceStateData state) {
  275 + addDeviceUsingState(state);
  276 + }
  277 +
  278 + @Override
  279 + public void onFailure(Throwable t) {
  280 + log.warn("Failed to register device to the state service", t);
  281 + }
  282 + });
  283 + }
  284 +
  285 + private void onDeviceUpdatedSync(Device device) {
  286 + DeviceStateData stateData = deviceStates.get(device.getId());
  287 + if (stateData != null) {
  288 + TbMsgMetaData md = new TbMsgMetaData();
  289 + md.putValue("deviceName", device.getName());
  290 + md.putValue("deviceType", device.getType());
  291 + stateData.setMetaData(md);
  292 + }
  293 + }
  294 +
  295 + private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) {
  296 + deviceStates.remove(deviceId);
  297 + Set<DeviceId> deviceIds = tenantDevices.get(tenantId);
  298 + if (deviceIds != null) {
  299 + deviceIds.remove(deviceId);
  300 + if (deviceIds.isEmpty()) {
  301 + tenantDevices.remove(tenantId);
  302 + }
  303 + }
  304 + }
  305 +
  306 + private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) {
  307 + ListenableFuture<List<AttributeKvEntry>> attributes = attributesService.find(device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES);
  308 + return Futures.transform(attributes, new Function<List<AttributeKvEntry>, DeviceStateData>() {
  309 + @Nullable
  310 + @Override
  311 + public DeviceStateData apply(@Nullable List<AttributeKvEntry> attributes) {
  312 + long lastActivityTime = getAttributeValue(attributes, LAST_ACTIVITY_TIME, 0L);
  313 + long inactivityAlarmTime = getAttributeValue(attributes, INACTIVITY_ALARM_TIME, 0L);
  314 + long inactivityTimeout = getAttributeValue(attributes, INACTIVITY_TIMEOUT, TimeUnit.SECONDS.toMillis(defaultInactivityTimeoutInSec));
  315 + boolean active = System.currentTimeMillis() < lastActivityTime + inactivityTimeout;
  316 + DeviceState deviceState = DeviceState.builder()
  317 + .active(active)
  318 + .lastConnectTime(getAttributeValue(attributes, LAST_CONNECT_TIME, 0L))
  319 + .lastDisconnectTime(getAttributeValue(attributes, LAST_DISCONNECT_TIME, 0L))
  320 + .lastActivityTime(lastActivityTime)
  321 + .lastInactivityAlarmTime(inactivityAlarmTime)
  322 + .inactivityTimeout(inactivityTimeout)
  323 + .build();
  324 + TbMsgMetaData md = new TbMsgMetaData();
  325 + md.putValue("deviceName", device.getName());
  326 + md.putValue("deviceType", device.getType());
  327 + return DeviceStateData.builder()
  328 + .tenantId(device.getTenantId())
  329 + .deviceId(device.getId())
  330 + .metaData(md)
  331 + .state(deviceState).build();
  332 + }
  333 + });
  334 + }
  335 +
  336 + private long getLastPersistTime(List<AttributeKvEntry> attributes) {
  337 + return attributes.stream().map(AttributeKvEntry::getLastUpdateTs).max(Long::compare).orElse(0L);
  338 + }
  339 +
  340 + private long getAttributeValue(List<AttributeKvEntry> attributes, String attributeName, long defaultValue) {
  341 + for (AttributeKvEntry attribute : attributes) {
  342 + if (attribute.getKey().equals(attributeName)) {
  343 + return attribute.getLongValue().orElse(defaultValue);
  344 + }
  345 + }
  346 + return defaultValue;
  347 + }
  348 +
  349 + private void pushRuleEngineMessage(DeviceStateData stateData, String msgType) {
  350 + DeviceState state = stateData.getState();
  351 + try {
  352 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), msgType, stateData.getDeviceId(), stateData.getMetaData(), TbMsgDataType.JSON
  353 + , json.writeValueAsString(state)
  354 + , null, null, 0L);
  355 + actorService.onMsg(new ServiceToRuleEngineMsg(stateData.getTenantId(), tbMsg));
  356 + } catch (Exception e) {
  357 + log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);
  358 + }
  359 + }
  360 +
  361 + private void saveAttribute(DeviceId deviceId, String key, long value) {
  362 + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value));
  363 + }
  364 +
  365 + private void saveAttribute(DeviceId deviceId, String key, boolean value) {
  366 + tsSubService.saveAttrAndNotify(deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(deviceId, key, value));
  367 + }
  368 +
  369 + private class AttributeSaveCallback implements FutureCallback<Void> {
  370 + private final DeviceId deviceId;
  371 + private final String key;
  372 + private final Object value;
  373 +
  374 + AttributeSaveCallback(DeviceId deviceId, String key, Object value) {
  375 + this.deviceId = deviceId;
  376 + this.key = key;
  377 + this.value = value;
  378 + }
  379 +
  380 + @Override
  381 + public void onSuccess(@Nullable Void result) {
  382 + log.trace("[{}] Successfully updated attribute [{}] with value [{}]", deviceId, key, value);
  383 + }
  384 +
  385 + @Override
  386 + public void onFailure(Throwable t) {
  387 + log.warn("[{}] Failed to update attribute [{}] with value [{}]", deviceId, key, value, t);
  388 + }
  389 + }
  390 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.state;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Data;
  20 +
  21 +/**
  22 + * Created by ashvayka on 01.05.18.
  23 + */
  24 +@Data
  25 +@Builder
  26 +public class DeviceState {
  27 +
  28 + private boolean active;
  29 + private long lastConnectTime;
  30 + private long lastActivityTime;
  31 + private long lastDisconnectTime;
  32 + private long lastInactivityAlarmTime;
  33 + private long inactivityTimeout;
  34 +
  35 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.state;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  23 +
  24 +/**
  25 + * Created by ashvayka on 01.05.18.
  26 + */
  27 +@Data
  28 +@Builder
  29 +class DeviceStateData {
  30 +
  31 + private final TenantId tenantId;
  32 + private final DeviceId deviceId;
  33 +
  34 + private TbMsgMetaData metaData;
  35 + private final DeviceState state;
  36 +
  37 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.state;
  17 +
  18 +import org.thingsboard.server.common.data.Device;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
  20 +
  21 +import java.util.Optional;
  22 +
  23 +/**
  24 + * Created by ashvayka on 01.05.18.
  25 + */
  26 +public interface DeviceStateService {
  27 +
  28 + void onDeviceAdded(Device device);
  29 +
  30 + void onDeviceUpdated(Device device);
  31 +
  32 + void onDeviceDeleted(Device device);
  33 +
  34 + void onDeviceConnect(DeviceId deviceId);
  35 +
  36 + void onDeviceActivity(DeviceId deviceId);
  37 +
  38 + void onDeviceDisconnect(DeviceId deviceId);
  39 +
  40 + void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout);
  41 +
  42 + Optional<DeviceState> getDeviceState(DeviceId deviceId);
  43 +
  44 +}