Commit 3c7295ee8e2ca2a085ff3a7338ad4f8c84437436

Authored by Andrew Shvayka
1 parent 69aac973

RPC

Showing 30 changed files with 754 additions and 149 deletions
... ... @@ -199,6 +199,10 @@ public class ActorSystemContext {
199 199 @Getter
200 200 private long queuePersistenceTimeout;
201 201
  202 + @Value("${actors.client_side_rpc.timeout}")
  203 + @Getter
  204 + private long clientSideRpcTimeout;
  205 +
202 206 @Value("${actors.rule.chain.error_persist_frequency}")
203 207 @Getter
204 208 private long ruleChainErrorPersistFrequency;
... ...
... ... @@ -25,12 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId;
25 25 import org.thingsboard.server.common.msg.TbActorMsg;
26 26 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
27 27 import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
  28 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
28 29 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
29   -import org.thingsboard.server.common.msg.timeout.DeviceActorRpcTimeoutMsg;
  30 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
30 31 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
31 32 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
32   -import org.thingsboard.server.common.msg.timeout.TimeoutMsg;
33   -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg;
  33 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  34 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
34 35
35 36 public class DeviceActor extends ContextAwareActor {
36 37
... ... @@ -62,10 +63,16 @@ public class DeviceActor extends ContextAwareActor {
62 63 processor.processNameOrTypeUpdate((DeviceNameOrTypeUpdateMsg) msg);
63 64 break;
64 65 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
65   - processor.processRpcRequest(context(), (ToDeviceRpcRequestMsg) msg);
  66 + processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
66 67 break;
67   - case DEVICE_ACTOR_RPC_TIMEOUT_MSG:
68   - processor.processRpcTimeout(context(), (DeviceActorRpcTimeoutMsg) msg);
  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);
69 76 break;
70 77 case DEVICE_ACTOR_QUEUE_TIMEOUT_MSG:
71 78 processor.processQueueTimeout(context(), (DeviceActorQueueTimeoutMsg) msg);
... ...
1 1 /**
2 2 * Copyright © 2016-2018 The Thingsboard Authors
3   - *
  3 + * <p>
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
... ... @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.ListenableFuture;
25 25 import com.google.gson.Gson;
26 26 import com.google.gson.JsonArray;
27 27 import com.google.gson.JsonObject;
  28 +import com.google.gson.JsonParser;
28 29 import org.thingsboard.server.actors.ActorSystemContext;
29 30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
30 31 import org.thingsboard.server.common.data.DataConstants;
... ... @@ -53,28 +54,28 @@ import org.thingsboard.server.common.msg.core.RuleEngineErrorMsg;
53 54 import org.thingsboard.server.common.msg.core.SessionCloseMsg;
54 55 import org.thingsboard.server.common.msg.core.SessionCloseNotification;
55 56 import org.thingsboard.server.common.msg.core.SessionOpenMsg;
56   -import org.thingsboard.server.common.msg.core.StatusCodeResponse;
57 57 import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
58 58 import org.thingsboard.server.common.msg.core.ToDeviceRpcRequestMsg;
59 59 import org.thingsboard.server.common.msg.core.ToDeviceRpcResponseMsg;
60 60 import org.thingsboard.server.common.msg.core.ToDeviceSessionActorMsg;
  61 +import org.thingsboard.server.common.msg.core.ToServerRpcRequestMsg;
61 62 import org.thingsboard.server.common.msg.device.DeviceToDeviceActorMsg;
62 63 import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg;
63 64 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
64 65 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
65 66 import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
66 67 import org.thingsboard.server.common.msg.session.SessionMsgType;
67   -import org.thingsboard.server.common.msg.session.SessionMsgType;
68 68 import org.thingsboard.server.common.msg.session.SessionType;
69 69 import org.thingsboard.server.common.msg.session.ToDeviceMsg;
  70 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
70 71 import org.thingsboard.server.common.msg.timeout.DeviceActorQueueTimeoutMsg;
71   -import org.thingsboard.server.common.msg.timeout.DeviceActorRpcTimeoutMsg;
  72 +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
72 73 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
73 74 import org.thingsboard.server.extensions.api.device.DeviceNameOrTypeUpdateMsg;
74   -import org.thingsboard.server.extensions.api.plugins.PluginCallback;
75   -import org.thingsboard.server.extensions.api.plugins.PluginContext;
76 75 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
77 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;
78 79
79 80 import javax.annotation.Nullable;
80 81 import java.util.ArrayList;
... ... @@ -87,7 +88,6 @@ import java.util.Map;
87 88 import java.util.Optional;
88 89 import java.util.Set;
89 90 import java.util.UUID;
90   -import java.util.concurrent.ExecutionException;
91 91 import java.util.concurrent.TimeoutException;
92 92 import java.util.function.Consumer;
93 93 import java.util.function.Predicate;
... ... @@ -103,10 +103,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
103 103 private final Map<SessionId, SessionInfo> sessions;
104 104 private final Map<SessionId, SessionInfo> attributeSubscriptions;
105 105 private final Map<SessionId, SessionInfo> rpcSubscriptions;
106   - private final Map<Integer, ToDeviceRpcRequestMetadata> rpcPendingMap;
  106 + private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
  107 + private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
107 108 private final Map<UUID, PendingSessionMsgData> pendingMsgs;
108 109
109 110 private final Gson gson = new Gson();
  111 + private final JsonParser jsonParser = new JsonParser();
110 112
111 113 private int rpcSeq = 0;
112 114 private String deviceName;
... ... @@ -120,7 +122,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
120 122 this.sessions = new HashMap<>();
121 123 this.attributeSubscriptions = new HashMap<>();
122 124 this.rpcSubscriptions = new HashMap<>();
123   - this.rpcPendingMap = new HashMap<>();
  125 + this.toDeviceRpcPendingMap = new HashMap<>();
  126 + this.toServerRpcPendingMap = new HashMap<>();
124 127 this.pendingMsgs = new HashMap<>();
125 128 initAttributes();
126 129 }
... ... @@ -134,7 +137,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
134 137 this.defaultMetaData.putValue("deviceType", deviceType);
135 138 }
136 139
137   - void processRpcRequest(ActorContext context, org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg msg) {
  140 + void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) {
138 141 ToDeviceRpcRequest request = msg.getMsg();
139 142 ToDeviceRpcRequestBody body = request.getBody();
140 143 ToDeviceRpcRequestMsg rpcRequest = new ToDeviceRpcRequestMsg(
... ... @@ -174,14 +177,14 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
174 177
175 178 }
176 179
177   - private void registerPendingRpcRequest(ActorContext context, org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
178   - rpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
179   - DeviceActorRpcTimeoutMsg timeoutMsg = new DeviceActorRpcTimeoutMsg(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);
180 183 scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
181 184 }
182 185
183   - void processRpcTimeout(ActorContext context, DeviceActorRpcTimeoutMsg msg) {
184   - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(msg.getId());
  186 + void processServerSideRpcTimeout(ActorContext context, DeviceActorServerSideRpcTimeoutMsg msg) {
  187 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
185 188 if (requestMd != null) {
186 189 logger.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
187 190 systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
... ... @@ -200,7 +203,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
200 203
201 204 void processQueueAck(ActorContext context, RuleEngineQueuePutAckMsg msg) {
202 205 PendingSessionMsgData data = pendingMsgs.remove(msg.getId());
203   - if (data != null) {
  206 + if (data != null && data.isReplyOnQueueAck()) {
204 207 logger.debug("[{}] Queue put [{}] ack detected!", deviceId, msg.getId());
205 208 ToDeviceMsg toDeviceMsg = BasicStatusCodeResponse.onSuccess(data.getSessionMsgType(), data.getRequestId());
206 209 sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServerAddress());
... ... @@ -208,8 +211,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
208 211 }
209 212
210 213 private void sendPendingRequests(ActorContext context, SessionId sessionId, SessionType type, Optional<ServerAddress> server) {
211   - if (!rpcPendingMap.isEmpty()) {
212   - logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, rpcPendingMap.size(), sessionId);
  214 + if (!toDeviceRpcPendingMap.isEmpty()) {
  215 + logger.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
213 216 if (type == SessionType.SYNC) {
214 217 logger.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
215 218 rpcSubscriptions.remove(sessionId);
... ... @@ -219,12 +222,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
219 222 }
220 223 Set<Integer> sentOneWayIds = new HashSet<>();
221 224 if (type == SessionType.ASYNC) {
222   - rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
  225 + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
223 226 } else {
224   - rpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
  227 + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, server, sentOneWayIds));
225 228 }
226 229
227   - sentOneWayIds.forEach(rpcPendingMap::remove);
  230 + sentOneWayIds.forEach(toDeviceRpcPendingMap::remove);
228 231 }
229 232
230 233 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
... ... @@ -263,8 +266,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
263 266 handlePostTelemetryRequest(context, msg);
264 267 break;
265 268 case TO_SERVER_RPC_REQUEST:
  269 + handleClientSideRPCRequest(context, msg);
266 270 break;
267   - //TODO: push to queue and start processing!
268 271 }
269 272 }
270 273 }
... ... @@ -345,11 +348,47 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
345 348 pushToRuleEngineWithTimeout(context, tbMsg, src, request);
346 349 }
347 350
  351 + private void handleClientSideRPCRequest(ActorContext context, DeviceToDeviceActorMsg src) {
  352 + ToServerRpcRequestMsg request = (ToServerRpcRequestMsg) src.getPayload();
  353 +
  354 + JsonObject json = new JsonObject();
  355 + json.addProperty("method", request.getMethod());
  356 + json.add("params", jsonParser.parse(request.getParams()));
  357 +
  358 + TbMsgMetaData requestMetaData = defaultMetaData.copy();
  359 + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
  360 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json));
  361 + pushToRuleEngineWithTimeout(context, tbMsg, src, request);
  362 +
  363 + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
  364 + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(src.getSessionId(), src.getSessionType(), src.getServerAddress()));
  365 + }
  366 +
  367 + public void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
  368 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
  369 + if (data != null) {
  370 + logger.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
  371 + ToDeviceMsg toDeviceMsg = new RuleEngineErrorMsg(SessionMsgType.TO_SERVER_RPC_REQUEST, RuleEngineError.TIMEOUT);
  372 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(toDeviceMsg, data.getSessionId()), data.getServer());
  373 + }
  374 + }
  375 +
  376 + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
  377 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getMsg().getRequestId());
  378 + if (data != null) {
  379 + sendMsgToSessionActor(new BasicToDeviceSessionActorMsg(msg.getMsg(), data.getSessionId()), data.getServer());
  380 + }
  381 + }
  382 +
348 383 private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, DeviceToDeviceActorMsg src, FromDeviceRequestMsg fromDeviceRequestMsg) {
  384 + pushToRuleEngineWithTimeout(context, tbMsg, src, fromDeviceRequestMsg, true);
  385 + }
  386 +
  387 + private void pushToRuleEngineWithTimeout(ActorContext context, TbMsg tbMsg, DeviceToDeviceActorMsg src, FromDeviceRequestMsg fromDeviceRequestMsg, boolean replyOnAck) {
349 388 SessionMsgType sessionMsgType = fromDeviceRequestMsg.getMsgType();
350 389 int requestId = fromDeviceRequestMsg.getRequestId();
351 390 if (systemContext.isQueuePersistenceEnabled()) {
352   - pendingMsgs.put(tbMsg.getId(), new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), sessionMsgType, requestId));
  391 + pendingMsgs.put(tbMsg.getId(), new PendingSessionMsgData(src.getSessionId(), src.getServerAddress(), sessionMsgType, requestId, replyOnAck));
353 392 scheduleMsgWithDelay(context, new DeviceActorQueueTimeoutMsg(tbMsg.getId(), systemContext.getQueuePersistenceTimeout()), systemContext.getQueuePersistenceTimeout());
354 393 } else {
355 394 ToDeviceSessionActorMsg response = new BasicToDeviceSessionActorMsg(BasicStatusCodeResponse.onSuccess(sessionMsgType, requestId), src.getSessionId());
... ... @@ -394,7 +433,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
394 433 if (inMsg.getMsgType() == SessionMsgType.TO_DEVICE_RPC_RESPONSE) {
395 434 logger.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
396 435 ToDeviceRpcResponseMsg responseMsg = (ToDeviceRpcResponseMsg) inMsg;
397   - ToDeviceRpcRequestMetadata requestMd = rpcPendingMap.remove(responseMsg.getRequestId());
  436 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
398 437 boolean success = requestMd != null;
399 438 if (success) {
400 439 systemContext.getDeviceRpcService().process(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), responseMsg.getData(), null));
... ...
... ... @@ -32,5 +32,6 @@ public final class PendingSessionMsgData {
32 32 private final Optional<ServerAddress> serverAddress;
33 33 private final SessionMsgType sessionMsgType;
34 34 private final int requestId;
  35 + private final boolean replyOnQueueAck;
35 36
36 37 }
... ...
... ... @@ -16,13 +16,13 @@
16 16 package org.thingsboard.server.actors.device;
17 17
18 18 import lombok.Data;
19   -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg;
  19 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
20 20
21 21 /**
22 22 * @author Andrew Shvayka
23 23 */
24 24 @Data
25 25 public class ToDeviceRpcRequestMetadata {
26   - private final ToDeviceRpcRequestMsg msg;
  26 + private final ToDeviceRpcRequestActorMsg msg;
27 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 +}
... ...
... ... @@ -37,7 +37,7 @@ import org.thingsboard.server.extensions.api.plugins.rpc.RpcMsg;
37 37 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
38 38 import org.thingsboard.server.service.cluster.rpc.GrpcSession;
39 39 import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
40   -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg;
  40 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
41 41
42 42 import java.io.Serializable;
43 43 import java.util.UUID;
... ... @@ -142,14 +142,14 @@ public class BasicRpcSessionListener implements GrpcSessionListener {
142 142 return new UUID(uid.getPluginUuidMsb(), uid.getPluginUuidLsb());
143 143 }
144 144
145   - private static ToDeviceRpcRequestMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
  145 + private static ToDeviceRpcRequestActorMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToDeviceRpcRequestRpcMessage msg) {
146 146 TenantId deviceTenantId = new TenantId(toUUID(msg.getDeviceTenantId()));
147 147 DeviceId deviceId = new DeviceId(toUUID(msg.getDeviceId()));
148 148
149 149 ToDeviceRpcRequestBody requestBody = new ToDeviceRpcRequestBody(msg.getMethod(), msg.getParams());
150 150 ToDeviceRpcRequest request = new ToDeviceRpcRequest(toUUID(msg.getMsgId()), deviceTenantId, deviceId, msg.getOneway(), msg.getExpTime(), requestBody);
151 151
152   - return new ToDeviceRpcRequestMsg(serverAddress, request);
  152 + return new ToDeviceRpcRequestActorMsg(serverAddress, request);
153 153 }
154 154
155 155 private static ToPluginRpcResponseDeviceMsg deserialize(ServerAddress serverAddress, ClusterAPIProtos.ToPluginRpcResponseRpcMessage msg) {
... ...
1 1 /**
2 2 * Copyright © 2016-2018 The Thingsboard Authors
3   - *
  3 + * <p>
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
... ... @@ -16,15 +16,20 @@
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 18 import akka.actor.ActorRef;
19   -import akka.actor.Cancellable;
  19 +import com.datastax.driver.core.utils.UUIDs;
20 20 import com.google.common.base.Function;
21 21 import org.thingsboard.rule.engine.api.*;
22 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 25 import org.thingsboard.server.common.data.id.RuleNodeId;
24 26 import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
25 28 import org.thingsboard.server.common.data.rule.RuleNode;
26 29 import org.thingsboard.server.common.msg.TbMsg;
  30 +import org.thingsboard.server.common.msg.TbMsgMetaData;
27 31 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  32 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
28 33 import org.thingsboard.server.dao.alarm.AlarmService;
29 34 import org.thingsboard.server.dao.asset.AssetService;
30 35 import org.thingsboard.server.dao.attributes.AttributesService;
... ... @@ -41,6 +46,7 @@ import scala.concurrent.duration.Duration;
41 46 import java.util.List;
42 47 import java.util.Set;
43 48 import java.util.concurrent.TimeUnit;
  49 +import java.util.function.Consumer;
44 50
45 51 /**
46 52 * Created by ashvayka on 19.03.18.
... ... @@ -113,6 +119,11 @@ class DefaultTbContext implements TbContext {
113 119 }
114 120
115 121 @Override
  122 + public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
  123 + return new TbMsg(UUIDs.timeBased(), type, originator, metaData, data);
  124 + }
  125 +
  126 + @Override
116 127 public RuleNodeId getSelfId() {
117 128 return nodeCtx.getSelf().getId();
118 129 }
... ... @@ -206,4 +217,29 @@ class DefaultTbContext implements TbContext {
206 217 public MailService getMailService() {
207 218 return mainCtx.getMailService();
208 219 }
  220 +
  221 + @Override
  222 + public RuleEngineRpcService getRpcService() {
  223 + return new RuleEngineRpcService() {
  224 + @Override
  225 + public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
  226 + mainCtx.getDeviceRpcService().sendRpcReplyToDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
  227 + }
  228 +
  229 + @Override
  230 + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
  231 + ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(),
  232 + src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
  233 + mainCtx.getDeviceRpcService().process(request, response -> {
  234 + consumer.accept(RuleEngineDeviceRpcResponse.builder()
  235 + .deviceId(src.getDeviceId())
  236 + .requestId(src.getRequestId())
  237 + .error(response.getError())
  238 + .response(response.getResponse())
  239 + .build());
  240 + });
  241 + }
  242 +
  243 + };
  244 + }
209 245 }
... ...
... ... @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.http.HttpStatus;
24 24 import org.springframework.http.ResponseEntity;
25 25 import org.springframework.security.access.prepost.PreAuthorize;
  26 +import org.springframework.util.StringUtils;
26 27 import org.springframework.web.bind.annotation.PathVariable;
27 28 import org.springframework.web.bind.annotation.RequestBody;
28 29 import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -30,18 +31,22 @@ import org.springframework.web.bind.annotation.RequestMethod;
30 31 import org.springframework.web.bind.annotation.ResponseBody;
31 32 import org.springframework.web.bind.annotation.RestController;
32 33 import org.springframework.web.context.request.async.DeferredResult;
33   -import org.thingsboard.server.actors.plugin.ValidationResult;
  34 +import org.thingsboard.server.common.data.audit.ActionType;
34 35 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
35 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
36 37 import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.EntityId;
37 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;
38 42 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
39 43 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
40 44 import org.thingsboard.server.extensions.api.exception.ToErrorResponseEntity;
41 45 import org.thingsboard.server.extensions.api.plugins.PluginConstants;
42   -import org.thingsboard.server.common.data.rpc.RpcRequest;
43   -import org.thingsboard.server.service.rpc.LocalRequestMetaData;
  46 +import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
  47 +import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
44 48 import org.thingsboard.server.service.rpc.DeviceRpcService;
  49 +import org.thingsboard.server.service.rpc.LocalRequestMetaData;
45 50 import org.thingsboard.server.service.security.AccessValidator;
46 51 import org.thingsboard.server.service.security.model.SecurityUser;
47 52
... ... @@ -53,6 +58,7 @@ import java.util.Optional;
53 58 import java.util.UUID;
54 59 import java.util.concurrent.ExecutorService;
55 60 import java.util.concurrent.Executors;
  61 +import java.util.function.Consumer;
56 62
57 63 /**
58 64 * Created by ashvayka on 22.03.18.
... ... @@ -117,7 +123,6 @@ public class RpcController extends BaseController {
117 123 accessValidator.validate(currentUser, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
118 124 @Override
119 125 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
120   -
121 126 ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(UUID.randomUUID(),
122 127 tenantId,
123 128 deviceId,
... ... @@ -125,7 +130,13 @@ public class RpcController extends BaseController {
125 130 timeout,
126 131 body
127 132 );
128   - deviceRpcService.process(rpcRequest, new LocalRequestMetaData(rpcRequest, currentUser, result));
  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 + });
129 140 }
130 141
131 142 @Override
... ... @@ -136,7 +147,7 @@ public class RpcController extends BaseController {
136 147 } else {
137 148 entity = new ResponseEntity(HttpStatus.UNAUTHORIZED);
138 149 }
139   - deviceRpcService.logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e);
  150 + logRpcCall(currentUser, deviceId, body, oneWay, Optional.empty(), e);
140 151 response.setResult(entity);
141 152 }
142 153 }));
... ... @@ -146,4 +157,69 @@ public class RpcController extends BaseController {
146 157 }
147 158 }
148 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 +
149 225 }
... ...
... ... @@ -40,7 +40,7 @@ import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
40 40 import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
41 41 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
42 42 import org.thingsboard.server.service.cluster.discovery.ServerInstanceService;
43   -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg;
  43 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
44 44
45 45 import javax.annotation.PreDestroy;
46 46 import java.io.IOException;
... ... @@ -132,7 +132,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
132 132 }
133 133
134 134 @Override
135   - public void tell(ServerAddress serverAddress, ToDeviceRpcRequestMsg toForward) {
  135 + public void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward) {
136 136 ClusterAPIProtos.ToRpcServerMessage msg = ClusterAPIProtos.ToRpcServerMessage.newBuilder()
137 137 .setToDeviceRpcRequestRpcMsg(toProtoMsg(toForward)).build();
138 138 tell(serverAddress, msg);
... ... @@ -196,7 +196,7 @@ public class ClusterGrpcService extends ClusterRpcServiceGrpc.ClusterRpcServiceI
196 196 ).build();
197 197 }
198 198
199   - private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestMsg msg) {
  199 + private static ClusterAPIProtos.ToDeviceRpcRequestRpcMessage toProtoMsg(ToDeviceRpcRequestActorMsg msg) {
200 200 ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.Builder builder = ClusterAPIProtos.ToDeviceRpcRequestRpcMessage.newBuilder();
201 201 ToDeviceRpcRequest request = msg.getMsg();
202 202
... ...
... ... @@ -24,7 +24,7 @@ import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg
24 24 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginRpcResponseDeviceMsg;
25 25 import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
26 26 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
27   -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestMsg;
  27 +import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
28 28
29 29 import java.util.UUID;
30 30
... ... @@ -41,7 +41,7 @@ public interface ClusterRpcService {
41 41
42 42 void tell(ServerAddress serverAddress, ToDeviceActorNotificationMsg toForward);
43 43
44   - void tell(ServerAddress serverAddress, ToDeviceRpcRequestMsg toForward);
  44 + void tell(ServerAddress serverAddress, ToDeviceRpcRequestActorMsg toForward);
45 45
46 46 void tell(ServerAddress serverAddress, ToPluginRpcResponseDeviceMsg toForward);
47 47
... ...
1 1 /**
2 2 * Copyright © 2016-2018 The Thingsboard Authors
3   - *
  3 + * <p>
4 4 * Licensed under the Apache License, Version 2.0 (the "License");
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
  7 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
12 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.service.rpc;
17 17
18   -import akka.actor.ActorRef;
19 18 import com.fasterxml.jackson.databind.ObjectMapper;
20 19 import lombok.extern.slf4j.Slf4j;
21 20 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -28,12 +27,15 @@ import org.thingsboard.server.actors.service.ActorService;
28 27 import org.thingsboard.server.common.data.audit.ActionType;
29 28 import org.thingsboard.server.common.data.id.DeviceId;
30 29 import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
31 31 import org.thingsboard.server.common.data.id.UUIDBased;
32 32 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
33 33 import org.thingsboard.server.common.msg.cluster.ServerAddress;
  34 +import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg;
34 35 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
35 36 import org.thingsboard.server.controller.BaseController;
36 37 import org.thingsboard.server.dao.audit.AuditLogService;
  38 +import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
37 39 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
38 40 import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
39 41 import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
... ... @@ -51,6 +53,7 @@ import java.util.concurrent.Executors;
51 53 import java.util.concurrent.ScheduledExecutorService;
52 54 import java.util.concurrent.TimeUnit;
53 55 import java.util.function.BiConsumer;
  56 +import java.util.function.Consumer;
54 57
55 58 /**
56 59 * Created by ashvayka on 27.03.18.
... ... @@ -59,8 +62,6 @@ import java.util.function.BiConsumer;
59 62 @Slf4j
60 63 public class DefaultDeviceRpcService implements DeviceRpcService {
61 64
62   - private static final ObjectMapper jsonMapper = new ObjectMapper();
63   -
64 65 @Autowired
65 66 private ClusterRoutingService routingService;
66 67
... ... @@ -75,7 +76,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
75 76
76 77 private ScheduledExecutorService rpcCallBackExecutor;
77 78
78   - private final ConcurrentMap<UUID, LocalRequestMetaData> localRpcRequests = new ConcurrentHashMap<>();
  79 + private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localRpcRequests = new ConcurrentHashMap<>();
79 80
80 81
81 82 @PostConstruct
... ... @@ -91,18 +92,18 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
91 92 }
92 93
93 94 @Override
94   - public void process(ToDeviceRpcRequest request, LocalRequestMetaData metaData) {
  95 + public void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) {
95 96 log.trace("[{}] Processing local rpc call for device [{}]", request.getTenantId(), request.getDeviceId());
96 97 sendRpcRequest(request);
97 98 UUID requestId = request.getId();
98   - localRpcRequests.put(requestId, metaData);
  99 + localRpcRequests.put(requestId, responseConsumer);
99 100 long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis());
100 101 log.error("[{}] processing the request: [{}]", this.hashCode(), requestId);
101 102 rpcCallBackExecutor.schedule(() -> {
102 103 log.error("[{}] timeout the request: [{}]", this.hashCode(), requestId);
103   - LocalRequestMetaData localMetaData = localRpcRequests.remove(requestId);
104   - if (localMetaData != null) {
105   - reply(localMetaData, new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT));
  104 + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId);
  105 + if (consumer != null) {
  106 + consumer.accept(new FromDeviceRpcResponse(requestId, null, RpcError.TIMEOUT));
106 107 }
107 108 }, timeout, TimeUnit.MILLISECONDS);
108 109 }
... ... @@ -123,58 +124,27 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
123 124 log.error("[{}] response the request: [{}]", this.hashCode(), response.getId());
124 125 //TODO: send to another server if needed.
125 126 UUID requestId = response.getId();
126   - LocalRequestMetaData md = localRpcRequests.remove(requestId);
127   - if (md != null) {
128   - log.trace("[{}] Processing local rpc response from device [{}]", requestId, md.getRequest().getDeviceId());
129   - reply(md, response);
  127 + Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId);
  128 + if (consumer != null) {
  129 + consumer.accept(response);
130 130 } else {
131 131 log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response);
132 132 }
133 133 }
134 134
135   - public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) {
136   - Optional<RpcError> rpcError = response.getError();
137   - DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
138   - if (rpcError.isPresent()) {
139   - logRpcCall(rpcRequest, rpcError, null);
140   - RpcError error = rpcError.get();
141   - switch (error) {
142   - case TIMEOUT:
143   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
144   - break;
145   - case NO_ACTIVE_CONNECTION:
146   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT));
147   - break;
148   - default:
149   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
150   - break;
151   - }
152   - } else {
153   - Optional<String> responseData = response.getResponse();
154   - if (responseData.isPresent() && !StringUtils.isEmpty(responseData.get())) {
155   - String data = responseData.get();
156   - try {
157   - logRpcCall(rpcRequest, rpcError, null);
158   - responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK));
159   - } catch (IOException e) {
160   - log.debug("Failed to decode device response: {}", data, e);
161   - logRpcCall(rpcRequest, rpcError, e);
162   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));
163   - }
164   - } else {
165   - logRpcCall(rpcRequest, rpcError, null);
166   - responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK));
167   - }
168   - }
  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);
169 139 }
170 140
171 141 private void sendRpcRequest(ToDeviceRpcRequest msg) {
172 142 log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg);
173   - ToDeviceRpcRequestMsg rpcMsg = new ToDeviceRpcRequestMsg(msg);
  143 + ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(msg);
174 144 forward(msg.getDeviceId(), rpcMsg, rpcService::tell);
175 145 }
176 146
177   - private void forward(DeviceId deviceId, ToDeviceRpcRequestMsg msg, BiConsumer<ServerAddress, ToDeviceRpcRequestMsg> rpcFunction) {
  147 + private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {
178 148 Optional<ServerAddress> instance = routingService.resolveById(deviceId);
179 149 if (instance.isPresent()) {
180 150 log.trace("[{}] Forwarding msg {} to remote device actor!", msg.getTenantId(), msg);
... ... @@ -184,32 +154,4 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
184 154 actorService.onMsg(msg);
185 155 }
186 156 }
187   -
188   - private void logRpcCall(LocalRequestMetaData rpcRequest, Optional<RpcError> rpcError, Throwable e) {
189   - logRpcCall(rpcRequest.getUser(), rpcRequest.getRequest().getDeviceId(), rpcRequest.getRequest().getBody(), rpcRequest.getRequest().isOneway(), rpcError, null);
190   - }
191   -
192   - @Override
193   - public void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e) {
194   - String rpcErrorStr = "";
195   - if (rpcError.isPresent()) {
196   - rpcErrorStr = "RPC Error: " + rpcError.get().name();
197   - }
198   - String method = body.getMethod();
199   - String params = body.getParams();
200   -
201   - auditLogService.logEntityAction(
202   - user.getTenantId(),
203   - user.getCustomerId(),
204   - user.getId(),
205   - user.getName(),
206   - (UUIDBased & EntityId) entityId,
207   - null,
208   - ActionType.RPC_CALL,
209   - BaseController.toException(e),
210   - rpcErrorStr,
211   - oneWay,
212   - method,
213   - params);
214   - }
215 157 }
... ...
... ... @@ -15,26 +15,25 @@
15 15 */
16 16 package org.thingsboard.server.service.rpc;
17 17
18   -import org.thingsboard.server.common.data.id.EntityId;
19   -import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  18 +import org.thingsboard.server.common.data.id.DeviceId;
  19 +import org.thingsboard.server.common.data.id.TenantId;
20 20 import org.thingsboard.server.common.msg.cluster.ServerAddress;
21 21 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
22 22 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
23   -import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
24   -import org.thingsboard.server.service.security.model.SecurityUser;
25 23
26   -import java.util.Optional;
  24 +import java.util.function.Consumer;
27 25
28 26 /**
29 27 * Created by ashvayka on 16.04.18.
30 28 */
31 29 public interface DeviceRpcService {
32 30
33   - void process(ToDeviceRpcRequest request, LocalRequestMetaData metaData);
  31 + void process(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer);
34 32
35 33 void process(ToDeviceRpcRequest request, ServerAddress originator);
36 34
37 35 void process(FromDeviceRpcResponse response);
38 36
39   - void logRpcCall(SecurityUser user, EntityId entityId, ToDeviceRpcRequestBody body, boolean oneWay, Optional<RpcError> rpcError, Throwable e);
  37 + void sendRpcReplyToDevice(TenantId tenantId, DeviceId deviceId, int requestId, String body);
  38 +
40 39 }
... ...
application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java renamed from application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestMsg.java
... ... @@ -32,13 +32,13 @@ import java.util.Optional;
32 32 */
33 33 @ToString
34 34 @RequiredArgsConstructor
35   -public class ToDeviceRpcRequestMsg implements ToDeviceActorNotificationMsg {
  35 +public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg {
36 36
37 37 private final ServerAddress serverAddress;
38 38 @Getter
39 39 private final ToDeviceRpcRequest msg;
40 40
41   - public ToDeviceRpcRequestMsg(ToDeviceRpcRequest msg) {
  41 + public ToDeviceRpcRequestActorMsg(ToDeviceRpcRequest msg) {
42 42 this(null, msg);
43 43 }
44 44
... ...
  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 +}
... ...
... ... @@ -229,6 +229,8 @@ actors:
229 229 enabled: "${ACTORS_QUEUE_ENABLED:true}"
230 230 # Maximum allowed timeout for persistence into the queue
231 231 timeout: "${ACTORS_QUEUE_PERSISTENCE_TIMEOUT:30000}"
  232 + client_side_rpc:
  233 + timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}"
232 234
233 235 cache:
234 236 # caffeine or redis
... ...
... ... @@ -75,7 +75,11 @@ public enum MsgType {
75 75
76 76 DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG,
77 77
78   - DEVICE_ACTOR_RPC_TIMEOUT_MSG,
  78 + SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG,
  79 +
  80 + DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG,
  81 +
  82 + DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG,
79 83
80 84 DEVICE_ACTOR_QUEUE_TIMEOUT_MSG,
81 85
... ...
... ... @@ -21,7 +21,7 @@ package org.thingsboard.server.common.msg.core;
21 21
22 22 public enum RuleEngineError {
23 23
24   - QUEUE_PUT_TIMEOUT(true), SERVER_ERROR(true);
  24 + QUEUE_PUT_TIMEOUT(true), SERVER_ERROR(true), TIMEOUT;
25 25
26 26 private final boolean critical;
27 27
... ...
... ... @@ -44,6 +44,8 @@ public class RuleEngineErrorMsg implements ToDeviceMsg {
44 44 return "Timeout during persistence of the message to the queue!";
45 45 case SERVER_ERROR:
46 46 return "Error during processing of message by the server!";
  47 + case TIMEOUT:
  48 + return "Timeout during processing of message by the server!";
47 49 default:
48 50 throw new RuntimeException("Error " + error + " is not supported!");
49 51 }
... ...
common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorClientSideRpcTimeoutMsg.java renamed from common/message/src/main/java/org/thingsboard/server/common/msg/timeout/DeviceActorRpcTimeoutMsg.java
... ... @@ -20,14 +20,14 @@ import org.thingsboard.server.common.msg.MsgType;
20 20 /**
21 21 * @author Andrew Shvayka
22 22 */
23   -public final class DeviceActorRpcTimeoutMsg extends TimeoutMsg<Integer> {
  23 +public final class DeviceActorClientSideRpcTimeoutMsg extends TimeoutMsg<Integer> {
24 24
25   - public DeviceActorRpcTimeoutMsg(Integer id, long timeout) {
  25 + public DeviceActorClientSideRpcTimeoutMsg(Integer id, long timeout) {
26 26 super(id, timeout);
27 27 }
28 28
29 29 @Override
30 30 public MsgType getMsgType() {
31   - return MsgType.DEVICE_ACTOR_RPC_TIMEOUT_MSG;
  31 + return MsgType.DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG;
32 32 }
33 33 }
... ...
  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.common.msg.timeout;
  17 +
  18 +import org.thingsboard.server.common.msg.MsgType;
  19 +
  20 +/**
  21 + * @author Andrew Shvayka
  22 + */
  23 +public final class DeviceActorServerSideRpcTimeoutMsg extends TimeoutMsg<Integer> {
  24 +
  25 + public DeviceActorServerSideRpcTimeoutMsg(Integer id, long timeout) {
  26 + super(id, timeout);
  27 + }
  28 +
  29 + @Override
  30 + public MsgType getMsgType() {
  31 + return MsgType.DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG;
  32 + }
  33 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  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 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  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.rule.engine.api;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +
  22 +/**
  23 + * Created by ashvayka on 02.04.18.
  24 + */
  25 +@Data
  26 +@Builder
  27 +public final class RuleEngineDeviceRpcRequest {
  28 +
  29 + private final DeviceId deviceId;
  30 + private final int requestId;
  31 + private final boolean oneway;
  32 + private final String method;
  33 + private final String body;
  34 + private final long timeout;
  35 +
  36 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  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 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  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.rule.engine.api;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.extensions.api.plugins.msg.RpcError;
  22 +
  23 +import java.util.Optional;
  24 +
  25 +/**
  26 + * Created by ashvayka on 02.04.18.
  27 + */
  28 +@Data
  29 +@Builder
  30 +public final class RuleEngineDeviceRpcResponse {
  31 +
  32 + private final DeviceId deviceId;
  33 + private final int requestId;
  34 + private final Optional<String> response;
  35 + private final Optional<RpcError> error;
  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.rule.engine.api;
  17 +
  18 +import org.thingsboard.server.common.data.id.DeviceId;
  19 +import java.util.function.Consumer;
  20 +
  21 +/**
  22 + * Created by ashvayka on 02.04.18.
  23 + */
  24 +public interface RuleEngineRpcService {
  25 +
  26 + void sendRpcReply(DeviceId deviceId, int requestId, String body);
  27 +
  28 + void sendRpcRequest(RuleEngineDeviceRpcRequest request, Consumer<RuleEngineDeviceRpcResponse> consumer);
  29 +
  30 +}
... ...
... ... @@ -15,10 +15,12 @@
15 15 */
16 16 package org.thingsboard.rule.engine.api;
17 17
  18 +import org.thingsboard.server.common.data.id.EntityId;
18 19 import org.thingsboard.server.common.data.id.RuleNodeId;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.rule.RuleNode;
21 22 import org.thingsboard.server.common.msg.TbMsg;
  23 +import org.thingsboard.server.common.msg.TbMsgMetaData;
22 24 import org.thingsboard.server.common.msg.cluster.ServerAddress;
23 25 import org.thingsboard.server.dao.alarm.AlarmService;
24 26 import org.thingsboard.server.dao.asset.AssetService;
... ... @@ -58,6 +60,8 @@ public interface TbContext {
58 60
59 61 void updateSelf(RuleNode self);
60 62
  63 + TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data);
  64 +
61 65 RuleNodeId getSelfId();
62 66
63 67 TenantId getTenantId();
... ... @@ -78,6 +82,8 @@ public interface TbContext {
78 82
79 83 RuleChainService getRuleChainService();
80 84
  85 + RuleEngineRpcService getRpcService();
  86 +
81 87 RuleEngineTelemetryService getTelemetryService();
82 88
83 89 TimeseriesService getTimeseriesService();
... ...
  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.rule.engine.api;
  17 +
  18 +/**
  19 + * Created by ashvayka on 19.01.18.
  20 + */
  21 +public final class TbRelationTypes {
  22 +
  23 + public static String SUCCESS = "Success";
  24 + public static String FAILURE = "Failure";
  25 +
  26 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  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 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  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.rule.engine.rpc;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.util.StringUtils;
  20 +import org.thingsboard.rule.engine.TbNodeUtils;
  21 +import org.thingsboard.rule.engine.api.RuleNode;
  22 +import org.thingsboard.rule.engine.api.TbContext;
  23 +import org.thingsboard.rule.engine.api.TbNode;
  24 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  25 +import org.thingsboard.rule.engine.api.TbNodeException;
  26 +import org.thingsboard.server.common.data.EntityType;
  27 +import org.thingsboard.server.common.data.id.DeviceId;
  28 +import org.thingsboard.server.common.data.plugin.ComponentType;
  29 +import org.thingsboard.server.common.msg.TbMsg;
  30 +
  31 +@Slf4j
  32 +@RuleNode(
  33 + type = ComponentType.ACTION,
  34 + name = "rpc call reply",
  35 + configClazz = TbSendRpcReplyNodeConfiguration.class,
  36 + nodeDescription = "Sends reply to the RPC call from device",
  37 + nodeDetails = "Expects messages with any message type. Will forward message body to the device."
  38 +)
  39 +public class TbSendRPCReplyNode implements TbNode {
  40 +
  41 + private TbSendRpcReplyNodeConfiguration config;
  42 +
  43 + @Override
  44 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  45 + this.config = TbNodeUtils.convert(configuration, TbSendRpcReplyNodeConfiguration.class);
  46 + }
  47 +
  48 + @Override
  49 + public void onMsg(TbContext ctx, TbMsg msg) {
  50 + String requestIdStr = msg.getMetaData().getValue(config.getRequestIdMetaDataAttribute());
  51 + if (msg.getOriginator().getEntityType() != EntityType.DEVICE) {
  52 + ctx.tellError(msg, new RuntimeException("Message originator is not a device entity!"));
  53 + } else if (StringUtils.isEmpty(requestIdStr)) {
  54 + ctx.tellError(msg, new RuntimeException("Request id is not present in the metadata!"));
  55 + } else if (StringUtils.isEmpty(msg.getData())) {
  56 + ctx.tellError(msg, new RuntimeException("Request body is empty!"));
  57 + } else {
  58 + ctx.getRpcService().sendRpcReply(new DeviceId(msg.getOriginator().getId()), Integer.parseInt(requestIdStr), msg.getData());
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public void destroy() {
  64 + }
  65 +
  66 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + * <p>
  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 + * <p>
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + * <p>
  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.rule.engine.rpc;
  17 +
  18 +import com.google.gson.Gson;
  19 +import com.google.gson.JsonObject;
  20 +import com.google.gson.JsonParser;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.rule.engine.TbNodeUtils;
  23 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
  24 +import org.thingsboard.rule.engine.api.RuleNode;
  25 +import org.thingsboard.rule.engine.api.TbContext;
  26 +import org.thingsboard.rule.engine.api.TbNode;
  27 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  28 +import org.thingsboard.rule.engine.api.TbNodeException;
  29 +import org.thingsboard.rule.engine.api.TbRelationTypes;
  30 +import org.thingsboard.server.common.data.EntityType;
  31 +import org.thingsboard.server.common.data.id.DeviceId;
  32 +import org.thingsboard.server.common.data.plugin.ComponentType;
  33 +import org.thingsboard.server.common.msg.TbMsg;
  34 +
  35 +import java.util.Random;
  36 +import java.util.concurrent.TimeUnit;
  37 +
  38 +@Slf4j
  39 +@RuleNode(
  40 + type = ComponentType.ACTION,
  41 + name = "rpc call request",
  42 + configClazz = TbSendRpcReplyNodeConfiguration.class,
  43 + nodeDescription = "Sends one-way RPC call to device",
  44 + nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes."
  45 +)
  46 +public class TbSendRPCRequestNode implements TbNode {
  47 +
  48 + private Random random = new Random();
  49 + private Gson gson = new Gson();
  50 + private JsonParser jsonParser = new JsonParser();
  51 + private TbSendRpcRequestNodeConfiguration config;
  52 +
  53 + @Override
  54 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  55 + this.config = TbNodeUtils.convert(configuration, TbSendRpcRequestNodeConfiguration.class);
  56 + }
  57 +
  58 + @Override
  59 + public void onMsg(TbContext ctx, TbMsg msg) {
  60 + JsonObject json = jsonParser.parse(msg.getData()).getAsJsonObject();
  61 +
  62 + if (msg.getOriginator().getEntityType() != EntityType.DEVICE) {
  63 + ctx.tellError(msg, new RuntimeException("Message originator is not a device entity!"));
  64 + } else if (!json.has("method")) {
  65 + ctx.tellError(msg, new RuntimeException("Method is not present in the message!"));
  66 + } else if (!json.has("params")) {
  67 + ctx.tellError(msg, new RuntimeException("Params are not present in the message!"));
  68 + } else {
  69 + int requestId = json.has("requestId") ? json.get("requestId").getAsInt() : random.nextInt();
  70 + RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder()
  71 + .method(gson.toJson(json.get("method")))
  72 + .body(gson.toJson(json.get("params")))
  73 + .deviceId(new DeviceId(msg.getOriginator().getId()))
  74 + .requestId(requestId)
  75 + .timeout(TimeUnit.SECONDS.toMillis(config.getTimeoutInSeconds()))
  76 + .build();
  77 +
  78 + ctx.getRpcService().sendRpcRequest(request, ruleEngineDeviceRpcResponse -> {
  79 + if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
  80 + TbMsg next = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().get());
  81 + ctx.tellNext(next, TbRelationTypes.SUCCESS);
  82 + } else {
  83 + TbMsg next = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
  84 + ctx.tellNext(next, TbRelationTypes.FAILURE);
  85 + ctx.tellError(msg, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
  86 + }
  87 + });
  88 + }
  89 + }
  90 +
  91 + @Override
  92 + public void destroy() {
  93 + }
  94 +
  95 + private String wrap(String name, String body) {
  96 + JsonObject json = new JsonObject();
  97 + json.addProperty(name, body);
  98 + return gson.toJson(json);
  99 + }
  100 +
  101 +}
... ...
  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.rule.engine.rpc;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +import org.thingsboard.server.common.data.DataConstants;
  21 +
  22 +@Data
  23 +public class TbSendRpcReplyNodeConfiguration implements NodeConfiguration<TbSendRpcReplyNodeConfiguration> {
  24 +
  25 + private String requestIdMetaDataAttribute;
  26 +
  27 + @Override
  28 + public TbSendRpcReplyNodeConfiguration defaultConfiguration() {
  29 + TbSendRpcReplyNodeConfiguration configuration = new TbSendRpcReplyNodeConfiguration();
  30 + configuration.setRequestIdMetaDataAttribute("requestId");
  31 + return configuration;
  32 + }
  33 +}
... ...
  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.rule.engine.rpc;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +
  21 +@Data
  22 +public class TbSendRpcRequestNodeConfiguration implements NodeConfiguration<TbSendRpcRequestNodeConfiguration> {
  23 +
  24 + private int timeoutInSeconds;
  25 +
  26 + @Override
  27 + public TbSendRpcRequestNodeConfiguration defaultConfiguration() {
  28 + TbSendRpcRequestNodeConfiguration configuration = new TbSendRpcRequestNodeConfiguration();
  29 + configuration.setTimeoutInSeconds(60);
  30 + return configuration;
  31 + }
  32 +}
... ...