Commit 5e2667d3dec644d48e4fb45084b0fbf939b8fe2a
1 parent
b16a02d9
Implementation of Ts Websocket Service
Showing
10 changed files
with
461 additions
and
62 deletions
... | ... | @@ -61,12 +61,17 @@ import org.thingsboard.server.service.cluster.discovery.DiscoveryService; |
61 | 61 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
62 | 62 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
63 | 63 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
64 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
64 | 65 | |
66 | +import javax.annotation.PostConstruct; | |
67 | +import javax.annotation.PreDestroy; | |
65 | 68 | import java.io.IOException; |
66 | 69 | import java.io.PrintWriter; |
67 | 70 | import java.io.StringWriter; |
68 | 71 | import java.nio.charset.StandardCharsets; |
69 | 72 | import java.util.Optional; |
73 | +import java.util.concurrent.ExecutorService; | |
74 | +import java.util.concurrent.Executors; | |
70 | 75 | |
71 | 76 | @Slf4j |
72 | 77 | @Component |
... | ... | @@ -158,6 +163,10 @@ public class ActorSystemContext { |
158 | 163 | |
159 | 164 | @Autowired |
160 | 165 | @Getter |
166 | + private TelemetrySubscriptionService tsSubService; | |
167 | + | |
168 | + @Autowired | |
169 | + @Getter | |
161 | 170 | @Setter |
162 | 171 | private PluginWebSocketMsgEndpoint wsMsgEndpoint; |
163 | 172 | |
... | ... | @@ -224,6 +233,21 @@ public class ActorSystemContext { |
224 | 233 | @Getter |
225 | 234 | private final Config config; |
226 | 235 | |
236 | + @Getter | |
237 | + private ExecutorService tsCallBackExecutor; | |
238 | + | |
239 | + @PostConstruct | |
240 | + public void initExecutor() { | |
241 | + tsCallBackExecutor = Executors.newSingleThreadExecutor(); | |
242 | + } | |
243 | + | |
244 | + @PreDestroy | |
245 | + public void shutdownExecutor() { | |
246 | + if (tsCallBackExecutor != null) { | |
247 | + tsCallBackExecutor.shutdownNow(); | |
248 | + } | |
249 | + } | |
250 | + | |
227 | 251 | public ActorSystemContext() { |
228 | 252 | config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); |
229 | 253 | } |
... | ... | @@ -345,7 +369,7 @@ public class ActorSystemContext { |
345 | 369 | return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); |
346 | 370 | } |
347 | 371 | |
348 | - public ListeningExecutor getExecutor() { | |
372 | + public ListeningExecutor getJsExecutor() { | |
349 | 373 | //TODO: take thread count from yml. |
350 | 374 | return new JsExecutorService(1); |
351 | 375 | } | ... | ... |
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,12 +15,18 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.actors.ruleChain; |
17 | 17 | |
18 | -import akka.actor.ActorContext; | |
19 | 18 | import akka.actor.ActorRef; |
19 | +import com.google.common.base.Function; | |
20 | +import com.google.common.util.concurrent.FutureCallback; | |
21 | +import com.google.common.util.concurrent.Futures; | |
22 | +import com.google.common.util.concurrent.ListenableFuture; | |
20 | 23 | import org.thingsboard.rule.engine.api.ListeningExecutor; |
21 | 24 | import org.thingsboard.rule.engine.api.TbContext; |
22 | 25 | import org.thingsboard.server.actors.ActorSystemContext; |
26 | +import org.thingsboard.server.common.data.id.EntityId; | |
23 | 27 | import org.thingsboard.server.common.data.id.RuleNodeId; |
28 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
29 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
24 | 30 | import org.thingsboard.server.common.msg.TbMsg; |
25 | 31 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
26 | 32 | import org.thingsboard.server.dao.alarm.AlarmService; |
... | ... | @@ -35,6 +41,8 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; |
35 | 41 | import org.thingsboard.server.dao.user.UserService; |
36 | 42 | import scala.concurrent.duration.Duration; |
37 | 43 | |
44 | +import javax.annotation.Nullable; | |
45 | +import java.util.List; | |
38 | 46 | import java.util.Set; |
39 | 47 | import java.util.concurrent.TimeUnit; |
40 | 48 | |
... | ... | @@ -43,6 +51,7 @@ import java.util.concurrent.TimeUnit; |
43 | 51 | */ |
44 | 52 | class DefaultTbContext implements TbContext { |
45 | 53 | |
54 | + private static final Function<? super List<Void>, ? extends Void> LIST_VOID_FUNCTION = v -> null; | |
46 | 55 | private final ActorSystemContext mainCtx; |
47 | 56 | private final RuleNodeCtx nodeCtx; |
48 | 57 | |
... | ... | @@ -113,8 +122,35 @@ class DefaultTbContext implements TbContext { |
113 | 122 | } |
114 | 123 | |
115 | 124 | @Override |
125 | + public void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { | |
126 | + saveAndNotify(entityId, ts, 0L, callback); | |
127 | + } | |
128 | + | |
129 | + @Override | |
130 | + public void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { | |
131 | + ListenableFuture<List<Void>> saveFuture = mainCtx.getTsService().save(entityId, ts, ttl); | |
132 | + Futures.addCallback(saveFuture, new FutureCallback<List<Void>>() { | |
133 | + @Override | |
134 | + public void onSuccess(@Nullable List<Void> result) { | |
135 | + mainCtx.getTsSubService().onLocalTimeseriesUpdate(entityId, ts); | |
136 | + callback.onSuccess(null); | |
137 | + } | |
138 | + | |
139 | + @Override | |
140 | + public void onFailure(Throwable t) { | |
141 | + callback.onFailure(t); | |
142 | + } | |
143 | + }, mainCtx.getTsCallBackExecutor()); | |
144 | + } | |
145 | + | |
146 | + @Override | |
147 | + public void saveAndNotify(EntityId entityId, String scope, Set<AttributeKvEntry> attributes, FutureCallback<Void> callback) { | |
148 | + | |
149 | + } | |
150 | + | |
151 | + @Override | |
116 | 152 | public ListeningExecutor getJsExecutor() { |
117 | - return mainCtx.getExecutor(); | |
153 | + return mainCtx.getJsExecutor(); | |
118 | 154 | } |
119 | 155 | |
120 | 156 | @Override | ... | ... |
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.google.common.util.concurrent.FutureCallback; | |
19 | +import org.springframework.http.ResponseEntity; | |
20 | +import org.springframework.web.context.request.async.DeferredResult; | |
21 | +import org.thingsboard.server.service.security.ValidationCallback; | |
22 | + | |
23 | +/** | |
24 | + * Created by ashvayka on 21.02.17. | |
25 | + */ | |
26 | +public class HttpValidationCallback extends ValidationCallback<DeferredResult<ResponseEntity>> { | |
27 | + | |
28 | + public HttpValidationCallback(DeferredResult<ResponseEntity> response, FutureCallback<DeferredResult<ResponseEntity>> action) { | |
29 | + super(response, action); | |
30 | + } | |
31 | + | |
32 | +} | ... | ... |
... | ... | @@ -69,6 +69,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; |
69 | 69 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
70 | 70 | |
71 | 71 | import javax.annotation.Nullable; |
72 | +import javax.annotation.PostConstruct; | |
72 | 73 | import javax.annotation.PreDestroy; |
73 | 74 | import java.util.ArrayList; |
74 | 75 | import java.util.Arrays; |
... | ... | @@ -101,6 +102,7 @@ public class TelemetryController extends BaseController { |
101 | 102 | |
102 | 103 | private ExecutorService executor; |
103 | 104 | |
105 | + @PostConstruct | |
104 | 106 | public void initExecutor() { |
105 | 107 | executor = Executors.newSingleThreadExecutor(); |
106 | 108 | } | ... | ... |
... | ... | @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; |
22 | 22 | import org.thingsboard.server.common.data.id.RuleChainId; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 24 | import org.thingsboard.server.common.data.rule.RuleChain; |
25 | -import org.thingsboard.server.controller.ValidationCallback; | |
25 | +import org.thingsboard.server.controller.HttpValidationCallback; | |
26 | 26 | import org.thingsboard.server.dao.alarm.AlarmService; |
27 | 27 | import org.thingsboard.server.dao.asset.AssetService; |
28 | 28 | import org.thingsboard.server.dao.customer.CustomerService; |
... | ... | @@ -91,7 +91,7 @@ public class AccessValidator { |
91 | 91 | |
92 | 92 | final DeferredResult<ResponseEntity> response = new DeferredResult<>(); |
93 | 93 | |
94 | - validate(currentUser, entityId, new ValidationCallback(response, | |
94 | + validate(currentUser, entityId, new HttpValidationCallback(response, | |
95 | 95 | new FutureCallback<DeferredResult<ResponseEntity>>() { |
96 | 96 | @Override |
97 | 97 | public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { |
... | ... | @@ -107,7 +107,7 @@ public class AccessValidator { |
107 | 107 | return response; |
108 | 108 | } |
109 | 109 | |
110 | - public void validate(SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
110 | + public <T> void validate(SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
111 | 111 | switch (entityId.getEntityType()) { |
112 | 112 | case DEVICE: |
113 | 113 | validateDevice(currentUser, entityId, callback); |
... | ... | @@ -130,7 +130,7 @@ public class AccessValidator { |
130 | 130 | } |
131 | 131 | } |
132 | 132 | |
133 | - private void validateDevice(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
133 | + private void validateDevice(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
134 | 134 | if (currentUser.isSystemAdmin()) { |
135 | 135 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
136 | 136 | } else { |
... | ... | @@ -151,7 +151,7 @@ public class AccessValidator { |
151 | 151 | } |
152 | 152 | } |
153 | 153 | |
154 | - private void validateAsset(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
154 | + private <T> void validateAsset(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
155 | 155 | if (currentUser.isSystemAdmin()) { |
156 | 156 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
157 | 157 | } else { |
... | ... | @@ -173,7 +173,7 @@ public class AccessValidator { |
173 | 173 | } |
174 | 174 | |
175 | 175 | |
176 | - private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
176 | + private <T> void validateRuleChain(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
177 | 177 | if (currentUser.isCustomerUser()) { |
178 | 178 | callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
179 | 179 | } else { |
... | ... | @@ -194,7 +194,7 @@ public class AccessValidator { |
194 | 194 | } |
195 | 195 | } |
196 | 196 | |
197 | - private void validateCustomer(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
197 | + private <T> void validateCustomer(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
198 | 198 | if (currentUser.isSystemAdmin()) { |
199 | 199 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
200 | 200 | } else { |
... | ... | @@ -215,7 +215,7 @@ public class AccessValidator { |
215 | 215 | } |
216 | 216 | } |
217 | 217 | |
218 | - private void validateTenant(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) { | |
218 | + private <T> void validateTenant(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
219 | 219 | if (currentUser.isCustomerUser()) { |
220 | 220 | callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
221 | 221 | } else if (currentUser.isSystemAdmin()) { |
... | ... | @@ -234,7 +234,7 @@ public class AccessValidator { |
234 | 234 | } |
235 | 235 | } |
236 | 236 | |
237 | - private <T> FutureCallback<T> getCallback(ValidationCallback callback, Function<T, ValidationResult> transformer) { | |
237 | + private <T> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult> transformer) { | |
238 | 238 | return new FutureCallback<T>() { |
239 | 239 | @Override |
240 | 240 | public void onSuccess(@Nullable T result) { | ... | ... |
application/src/main/java/org/thingsboard/server/service/security/ValidationCallback.java
renamed from
application/src/main/java/org/thingsboard/server/controller/ValidationCallback.java
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; | |
1 | +package org.thingsboard.server.service.security; | |
17 | 2 | |
18 | 3 | import com.google.common.util.concurrent.FutureCallback; |
19 | -import org.springframework.http.ResponseEntity; | |
20 | -import org.springframework.web.context.request.async.DeferredResult; | |
21 | 4 | import org.thingsboard.server.actors.plugin.ValidationResult; |
22 | 5 | import org.thingsboard.server.actors.plugin.ValidationResultCode; |
23 | 6 | import org.thingsboard.server.extensions.api.exception.AccessDeniedException; |
... | ... | @@ -26,14 +9,14 @@ import org.thingsboard.server.extensions.api.exception.InternalErrorException; |
26 | 9 | import org.thingsboard.server.extensions.api.exception.UnauthorizedException; |
27 | 10 | |
28 | 11 | /** |
29 | - * Created by ashvayka on 21.02.17. | |
12 | + * Created by ashvayka on 31.03.18. | |
30 | 13 | */ |
31 | -public class ValidationCallback implements FutureCallback<ValidationResult> { | |
14 | +public class ValidationCallback<T> implements FutureCallback<ValidationResult> { | |
32 | 15 | |
33 | - private final DeferredResult<ResponseEntity> response; | |
34 | - private final FutureCallback<DeferredResult<ResponseEntity>> action; | |
16 | + private final T response; | |
17 | + private final FutureCallback<T> action; | |
35 | 18 | |
36 | - public ValidationCallback(DeferredResult<ResponseEntity> response, FutureCallback<DeferredResult<ResponseEntity>> action) { | |
19 | + public ValidationCallback(T response, FutureCallback<T> action) { | |
37 | 20 | this.response = response; |
38 | 21 | this.action = action; |
39 | 22 | } | ... | ... |
... | ... | @@ -5,8 +5,10 @@ import org.springframework.beans.factory.annotation.Autowired; |
5 | 5 | import org.springframework.stereotype.Service; |
6 | 6 | import org.thingsboard.server.common.data.id.EntityId; |
7 | 7 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
8 | +import org.thingsboard.server.common.data.kv.KvEntry; | |
8 | 9 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
9 | 10 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.Subscription; |
11 | +import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState; | |
10 | 12 | |
11 | 13 | import java.util.HashMap; |
12 | 14 | import java.util.List; |
... | ... | @@ -23,13 +25,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio |
23 | 25 | @Autowired |
24 | 26 | private TelemetryWebSocketService wsService; |
25 | 27 | |
26 | - | |
27 | 28 | private final Map<EntityId, Set<Subscription>> subscriptionsByEntityId = new HashMap<>(); |
28 | 29 | |
29 | 30 | private final Map<String, Map<Integer, Subscription>> subscriptionsByWsSessionId = new HashMap<>(); |
30 | 31 | |
31 | - | |
32 | - | |
33 | 32 | @Override |
34 | 33 | public void onAttributesUpdateFromServer(EntityId entityId, String scope, List<AttributeKvEntry> attributes) { |
35 | 34 | |
... | ... | @@ -39,4 +38,29 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio |
39 | 38 | public void onTimeseriesUpdateFromServer(EntityId entityId, List<TsKvEntry> entries) { |
40 | 39 | |
41 | 40 | } |
41 | + | |
42 | + @Override | |
43 | + public void cleanupLocalWsSessionSubscriptions(TelemetryWebSocketSessionRef sessionRef, String sessionId) { | |
44 | + | |
45 | + } | |
46 | + | |
47 | + @Override | |
48 | + public void removeSubscription(String sessionId, int cmdId) { | |
49 | + | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + public void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub) { | |
54 | + | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + public void onLocalTimeseriesUpdate(EntityId entityId, Map<Long, List<KvEntry>> ts) { | |
59 | + | |
60 | + } | |
61 | + | |
62 | + @Override | |
63 | + public void onLocalAttributesUpdate(EntityId entityId, String scope, Set<AttributeKvEntry> attributes) { | |
64 | + | |
65 | + } | |
42 | 66 | } | ... | ... |
... | ... | @@ -2,36 +2,46 @@ package org.thingsboard.server.service.telemetry; |
2 | 2 | |
3 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; |
4 | 4 | import com.fasterxml.jackson.databind.ObjectMapper; |
5 | +import com.google.common.base.Function; | |
5 | 6 | import com.google.common.util.concurrent.FutureCallback; |
7 | +import com.google.common.util.concurrent.Futures; | |
8 | +import com.google.common.util.concurrent.ListenableFuture; | |
9 | +import com.hazelcast.util.function.Consumer; | |
6 | 10 | import lombok.extern.slf4j.Slf4j; |
7 | 11 | import org.springframework.beans.factory.annotation.Autowired; |
8 | 12 | import org.springframework.stereotype.Service; |
9 | 13 | import org.springframework.util.StringUtils; |
14 | +import org.thingsboard.server.actors.plugin.ValidationResult; | |
10 | 15 | import org.thingsboard.server.common.data.DataConstants; |
11 | 16 | import org.thingsboard.server.common.data.id.EntityId; |
12 | 17 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
18 | +import org.thingsboard.server.common.data.kv.Aggregation; | |
13 | 19 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
20 | +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | |
14 | 21 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
15 | 22 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
23 | +import org.thingsboard.server.common.data.kv.TsKvQuery; | |
16 | 24 | import org.thingsboard.server.dao.attributes.AttributesService; |
17 | 25 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
18 | 26 | import org.thingsboard.server.extensions.api.exception.UnauthorizedException; |
19 | -import org.thingsboard.server.extensions.api.plugins.PluginCallback; | |
20 | -import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
21 | -import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRef; | |
22 | 27 | import org.thingsboard.server.extensions.api.plugins.ws.SessionEvent; |
23 | 28 | import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.AttributesSubscriptionCmd; |
29 | +import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.GetHistoryCmd; | |
24 | 30 | import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.SubscriptionCmd; |
25 | 31 | import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.TelemetryPluginCmd; |
26 | 32 | import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.TelemetryPluginCmdsWrapper; |
33 | +import org.thingsboard.server.extensions.core.plugin.telemetry.cmd.TimeseriesSubscriptionCmd; | |
27 | 34 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionErrorCode; |
28 | 35 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState; |
29 | 36 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; |
30 | 37 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionUpdate; |
38 | +import org.thingsboard.server.service.security.AccessValidator; | |
31 | 39 | |
40 | +import javax.annotation.Nullable; | |
41 | +import javax.annotation.PostConstruct; | |
42 | +import javax.annotation.PreDestroy; | |
32 | 43 | import java.io.IOException; |
33 | 44 | import java.util.ArrayList; |
34 | -import java.util.Arrays; | |
35 | 45 | import java.util.Collections; |
36 | 46 | import java.util.HashMap; |
37 | 47 | import java.util.HashSet; |
... | ... | @@ -41,6 +51,8 @@ import java.util.Optional; |
41 | 51 | import java.util.Set; |
42 | 52 | import java.util.concurrent.ConcurrentHashMap; |
43 | 53 | import java.util.concurrent.ConcurrentMap; |
54 | +import java.util.concurrent.ExecutorService; | |
55 | +import java.util.concurrent.Executors; | |
44 | 56 | import java.util.stream.Collectors; |
45 | 57 | |
46 | 58 | /** |
... | ... | @@ -50,6 +62,8 @@ import java.util.stream.Collectors; |
50 | 62 | @Slf4j |
51 | 63 | public class DefaultTelemetryWebSocketService implements TelemetryWebSocketService { |
52 | 64 | |
65 | + public static final int DEFAULT_LIMIT = 100; | |
66 | + public static final Aggregation DEFAULT_AGGREGATION = Aggregation.NONE; | |
53 | 67 | private static final int UNKNOWN_SUBSCRIPTION_ID = 0; |
54 | 68 | private static final String PROCESSING_MSG = "[{}] Processing: {}"; |
55 | 69 | private static final ObjectMapper jsonMapper = new ObjectMapper(); |
... | ... | @@ -66,11 +80,28 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
66 | 80 | private TelemetryWebSocketMsgEndpoint msgEndpoint; |
67 | 81 | |
68 | 82 | @Autowired |
83 | + private AccessValidator accessValidator; | |
84 | + | |
85 | + @Autowired | |
69 | 86 | private AttributesService attributesService; |
70 | 87 | |
71 | 88 | @Autowired |
72 | 89 | private TimeseriesService tsService; |
73 | 90 | |
91 | + private ExecutorService executor; | |
92 | + | |
93 | + @PostConstruct | |
94 | + public void initExecutor() { | |
95 | + executor = Executors.newSingleThreadExecutor(); | |
96 | + } | |
97 | + | |
98 | + @PreDestroy | |
99 | + public void shutdownExecutor() { | |
100 | + if (executor != null) { | |
101 | + executor.shutdownNow(); | |
102 | + } | |
103 | + } | |
104 | + | |
74 | 105 | @Override |
75 | 106 | public void handleWebSocketSessionEvent(TelemetryWebSocketSessionRef sessionRef, SessionEvent event) { |
76 | 107 | String sessionId = sessionRef.getSessionId(); |
... | ... | @@ -169,44 +200,190 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
169 | 200 | }; |
170 | 201 | |
171 | 202 | if (StringUtils.isEmpty(cmd.getScope())) { |
172 | - //ValidationCallback? | |
173 | - ctx.loadAttributes(entityId, Arrays.asList(DataConstants.allScopes()), keys, callback); | |
203 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, keys, callback)); | |
174 | 204 | } else { |
175 | - ctx.loadAttributes(entityId, cmd.getScope(), keys, callback); | |
205 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), keys, callback)); | |
206 | + } | |
207 | + } | |
208 | + | |
209 | + private void handleWsHistoryCmd(TelemetryWebSocketSessionRef sessionRef, GetHistoryCmd cmd) { | |
210 | + String sessionId = sessionRef.getSessionId(); | |
211 | + WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); | |
212 | + if (sessionMD == null) { | |
213 | + log.warn("[{}] Session meta data not found. ", sessionId); | |
214 | + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, | |
215 | + SESSION_META_DATA_NOT_FOUND); | |
216 | + sendWsMsg(sessionRef, update); | |
217 | + return; | |
218 | + } | |
219 | + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty() || cmd.getEntityType() == null || cmd.getEntityType().isEmpty()) { | |
220 | + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, | |
221 | + "Device id is empty!"); | |
222 | + sendWsMsg(sessionRef, update); | |
223 | + return; | |
224 | + } | |
225 | + if (cmd.getKeys() == null || cmd.getKeys().isEmpty()) { | |
226 | + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, | |
227 | + "Keys are empty!"); | |
228 | + sendWsMsg(sessionRef, update); | |
229 | + return; | |
176 | 230 | } |
231 | + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); | |
232 | + List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); | |
233 | + List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))) | |
234 | + .collect(Collectors.toList()); | |
235 | + | |
236 | + FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { | |
237 | + @Override | |
238 | + public void onSuccess(List<TsKvEntry> data) { | |
239 | + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); | |
240 | + } | |
241 | + | |
242 | + @Override | |
243 | + public void onFailure(Throwable e) { | |
244 | + SubscriptionUpdate update; | |
245 | + if (UnauthorizedException.class.isInstance(e)) { | |
246 | + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.UNAUTHORIZED, | |
247 | + SubscriptionErrorCode.UNAUTHORIZED.getDefaultMsg()); | |
248 | + } else { | |
249 | + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, | |
250 | + FAILED_TO_FETCH_DATA); | |
251 | + } | |
252 | + sendWsMsg(sessionRef, update); | |
253 | + } | |
254 | + }; | |
255 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, | |
256 | + on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); | |
177 | 257 | } |
178 | 258 | |
179 | - private void handleWsAttributesSubscription(PluginContext ctx, PluginWebsocketSessionRef sessionRef, | |
259 | + private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef, | |
180 | 260 | AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) { |
181 | - PluginCallback<List<AttributeKvEntry>> callback = new PluginCallback<List<AttributeKvEntry>>() { | |
261 | + FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<List<AttributeKvEntry>>() { | |
182 | 262 | @Override |
183 | - public void onSuccess(PluginContext ctx, List<AttributeKvEntry> data) { | |
263 | + public void onSuccess(List<AttributeKvEntry> data) { | |
184 | 264 | List<TsKvEntry> attributesData = data.stream().map(d -> new BasicTsKvEntry(d.getLastUpdateTs(), d)).collect(Collectors.toList()); |
185 | - sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), attributesData)); | |
265 | + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), attributesData)); | |
186 | 266 | |
187 | 267 | Map<String, Long> subState = new HashMap<>(attributesData.size()); |
188 | 268 | attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); |
189 | 269 | |
190 | 270 | SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.ATTRIBUTES, true, subState, cmd.getScope()); |
191 | - subscriptionManager.addLocalWsSubscription(ctx, sessionId, entityId, sub); | |
271 | + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); | |
192 | 272 | } |
193 | 273 | |
194 | 274 | @Override |
195 | - public void onFailure(PluginContext ctx, Exception e) { | |
275 | + public void onFailure(Throwable e) { | |
196 | 276 | log.error(FAILED_TO_FETCH_ATTRIBUTES, e); |
197 | 277 | SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, |
198 | 278 | FAILED_TO_FETCH_ATTRIBUTES); |
199 | - sendWsMsg(ctx, sessionRef, update); | |
279 | + sendWsMsg(sessionRef, update); | |
200 | 280 | } |
201 | 281 | }; |
202 | 282 | |
283 | + | |
203 | 284 | if (StringUtils.isEmpty(cmd.getScope())) { |
204 | - ctx.loadAttributes(entityId, Arrays.asList(DataConstants.allScopes()), callback); | |
285 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, callback)); | |
286 | + } else { | |
287 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, getAttributesFetchCallback(entityId, cmd.getScope(), callback)); | |
288 | + } | |
289 | + } | |
290 | + | |
291 | + private void handleWsTimeseriesSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd) { | |
292 | + String sessionId = sessionRef.getSessionId(); | |
293 | + log.debug("[{}] Processing: {}", sessionId, cmd); | |
294 | + | |
295 | + if (validateSessionMetadata(sessionRef, cmd, sessionId)) { | |
296 | + if (cmd.isUnsubscribe()) { | |
297 | + unsubscribe(sessionRef, cmd, sessionId); | |
298 | + } else if (validateSubscriptionCmd(sessionRef, cmd)) { | |
299 | + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); | |
300 | + Optional<Set<String>> keysOptional = getKeys(cmd); | |
301 | + | |
302 | + if (keysOptional.isPresent()) { | |
303 | + handleWsTimeseriesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId); | |
304 | + } else { | |
305 | + handleWsTimeseriesSubscription(sessionRef, cmd, sessionId, entityId); | |
306 | + } | |
307 | + } | |
308 | + } | |
309 | + } | |
310 | + | |
311 | + private void handleWsTimeseriesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef, | |
312 | + TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { | |
313 | + long startTs; | |
314 | + if (cmd.getTimeWindow() > 0) { | |
315 | + List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); | |
316 | + log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); | |
317 | + startTs = cmd.getStartTs(); | |
318 | + long endTs = cmd.getStartTs() + cmd.getTimeWindow(); | |
319 | + List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getInterval(), | |
320 | + getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); | |
321 | + | |
322 | + final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); | |
323 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, | |
324 | + on(r -> Futures.addCallback(tsService.findAll(entityId, queries), callback, executor), callback::onFailure)); | |
205 | 325 | } else { |
206 | - ctx.loadAttributes(entityId, cmd.getScope(), callback); | |
326 | + List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); | |
327 | + startTs = System.currentTimeMillis(); | |
328 | + log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId); | |
329 | + final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); | |
330 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, | |
331 | + on(r -> Futures.addCallback(tsService.findLatest(entityId, keys), callback, executor), callback::onFailure)); | |
207 | 332 | } |
208 | 333 | } |
209 | 334 | |
335 | + private void handleWsTimeseriesSubscription(TelemetryWebSocketSessionRef sessionRef, | |
336 | + TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { | |
337 | + FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { | |
338 | + @Override | |
339 | + public void onSuccess(List<TsKvEntry> data) { | |
340 | + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); | |
341 | + Map<String, Long> subState = new HashMap<>(data.size()); | |
342 | + data.forEach(v -> subState.put(v.getKey(), v.getTs())); | |
343 | + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, true, subState, cmd.getScope()); | |
344 | + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); | |
345 | + } | |
346 | + | |
347 | + @Override | |
348 | + public void onFailure(Throwable e) { | |
349 | + SubscriptionUpdate update; | |
350 | + if (UnauthorizedException.class.isInstance(e)) { | |
351 | + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.UNAUTHORIZED, | |
352 | + SubscriptionErrorCode.UNAUTHORIZED.getDefaultMsg()); | |
353 | + } else { | |
354 | + update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, | |
355 | + FAILED_TO_FETCH_DATA); | |
356 | + } | |
357 | + sendWsMsg(sessionRef, update); | |
358 | + } | |
359 | + }; | |
360 | + accessValidator.validate(sessionRef.getSecurityCtx(), entityId, | |
361 | + on(r -> Futures.addCallback(tsService.findAllLatest(entityId), callback, executor), callback::onFailure)); | |
362 | + } | |
363 | + | |
364 | + private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) { | |
365 | + return new FutureCallback<List<TsKvEntry>>() { | |
366 | + @Override | |
367 | + public void onSuccess(List<TsKvEntry> data) { | |
368 | + sendWsMsg(sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data)); | |
369 | + | |
370 | + Map<String, Long> subState = new HashMap<>(keys.size()); | |
371 | + keys.forEach(key -> subState.put(key, startTs)); | |
372 | + data.forEach(v -> subState.put(v.getKey(), v.getTs())); | |
373 | + SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, false, subState, cmd.getScope()); | |
374 | + subscriptionManager.addLocalWsSubscription(sessionId, entityId, sub); | |
375 | + } | |
376 | + | |
377 | + @Override | |
378 | + public void onFailure(Throwable e) { | |
379 | + log.error(FAILED_TO_FETCH_DATA, e); | |
380 | + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, | |
381 | + FAILED_TO_FETCH_DATA); | |
382 | + sendWsMsg(sessionRef, update); | |
383 | + } | |
384 | + }; | |
385 | + } | |
386 | + | |
210 | 387 | private void unsubscribe(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { |
211 | 388 | if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { |
212 | 389 | subscriptionManager.cleanupLocalWsSessionSubscriptions(sessionRef, sessionId); |
... | ... | @@ -258,4 +435,105 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
258 | 435 | } |
259 | 436 | } |
260 | 437 | |
438 | + private ListenableFuture<List<AttributeKvEntry>> mergeAllAttributesFutures(List<ListenableFuture<List<AttributeKvEntry>>> futures) { | |
439 | + return Futures.transform(Futures.successfulAsList(futures), | |
440 | + (Function<? super List<List<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> { | |
441 | + List<AttributeKvEntry> tmp = new ArrayList<>(); | |
442 | + if (input != null) { | |
443 | + input.forEach(tmp::addAll); | |
444 | + } | |
445 | + return tmp; | |
446 | + }, executor); | |
447 | + } | |
448 | + | |
449 | + private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) { | |
450 | + return new FutureCallback<ValidationResult>() { | |
451 | + @Override | |
452 | + public void onSuccess(@Nullable ValidationResult result) { | |
453 | + List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); | |
454 | + for (String scope : DataConstants.allScopes()) { | |
455 | + futures.add(attributesService.find(entityId, scope, keys)); | |
456 | + } | |
457 | + | |
458 | + ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures); | |
459 | + Futures.addCallback(future, callback); | |
460 | + } | |
461 | + | |
462 | + @Override | |
463 | + public void onFailure(Throwable t) { | |
464 | + callback.onFailure(t); | |
465 | + } | |
466 | + }; | |
467 | + } | |
468 | + | |
469 | + private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final String scope, final List<String> keys, final FutureCallback<List<AttributeKvEntry>> callback) { | |
470 | + return new FutureCallback<ValidationResult>() { | |
471 | + @Override | |
472 | + public void onSuccess(@Nullable ValidationResult result) { | |
473 | + Futures.addCallback(attributesService.find(entityId, scope, keys), callback); | |
474 | + } | |
475 | + | |
476 | + @Override | |
477 | + public void onFailure(Throwable t) { | |
478 | + callback.onFailure(t); | |
479 | + } | |
480 | + }; | |
481 | + } | |
482 | + | |
483 | + private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final FutureCallback<List<AttributeKvEntry>> callback) { | |
484 | + return new FutureCallback<ValidationResult>() { | |
485 | + @Override | |
486 | + public void onSuccess(@Nullable ValidationResult result) { | |
487 | + List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>(); | |
488 | + for (String scope : DataConstants.allScopes()) { | |
489 | + futures.add(attributesService.findAll(entityId, scope)); | |
490 | + } | |
491 | + | |
492 | + ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures); | |
493 | + Futures.addCallback(future, callback); | |
494 | + } | |
495 | + | |
496 | + @Override | |
497 | + public void onFailure(Throwable t) { | |
498 | + callback.onFailure(t); | |
499 | + } | |
500 | + }; | |
501 | + } | |
502 | + | |
503 | + private <T> FutureCallback<ValidationResult> getAttributesFetchCallback(final EntityId entityId, final String scope, final FutureCallback<List<AttributeKvEntry>> callback) { | |
504 | + return new FutureCallback<ValidationResult>() { | |
505 | + @Override | |
506 | + public void onSuccess(@Nullable ValidationResult result) { | |
507 | + Futures.addCallback(attributesService.findAll(entityId, scope), callback); | |
508 | + } | |
509 | + | |
510 | + @Override | |
511 | + public void onFailure(Throwable t) { | |
512 | + callback.onFailure(t); | |
513 | + } | |
514 | + }; | |
515 | + } | |
516 | + | |
517 | + private FutureCallback<ValidationResult> on(Consumer<ValidationResult> success, Consumer<Throwable> failure) { | |
518 | + return new FutureCallback<ValidationResult>() { | |
519 | + @Override | |
520 | + public void onSuccess(@Nullable ValidationResult result) { | |
521 | + success.accept(result); | |
522 | + } | |
523 | + | |
524 | + @Override | |
525 | + public void onFailure(Throwable t) { | |
526 | + failure.accept(t); | |
527 | + } | |
528 | + }; | |
529 | + } | |
530 | + | |
531 | + | |
532 | + private static Aggregation getAggregation(String agg) { | |
533 | + return StringUtils.isEmpty(agg) ? DEFAULT_AGGREGATION : Aggregation.valueOf(agg); | |
534 | + } | |
535 | + | |
536 | + private int getLimit(int limit) { | |
537 | + return limit == 0 ? DEFAULT_LIMIT : limit; | |
538 | + } | |
261 | 539 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
... | ... | @@ -2,10 +2,13 @@ package org.thingsboard.server.service.telemetry; |
2 | 2 | |
3 | 3 | import org.thingsboard.server.common.data.id.EntityId; |
4 | 4 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
5 | +import org.thingsboard.server.common.data.kv.KvEntry; | |
5 | 6 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
6 | 7 | import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionState; |
7 | 8 | |
8 | 9 | import java.util.List; |
10 | +import java.util.Map; | |
11 | +import java.util.Set; | |
9 | 12 | |
10 | 13 | /** |
11 | 14 | * Created by ashvayka on 27.03.18. |
... | ... | @@ -21,4 +24,8 @@ public interface TelemetrySubscriptionService { |
21 | 24 | void removeSubscription(String sessionId, int cmdId); |
22 | 25 | |
23 | 26 | void addLocalWsSubscription(String sessionId, EntityId entityId, SubscriptionState sub); |
27 | + | |
28 | + void onLocalTimeseriesUpdate(EntityId entityId, List<TsKvEntry> ts); | |
29 | + | |
30 | + void onLocalAttributesUpdate(EntityId entityId, String scope, Set<AttributeKvEntry> attributes); | |
24 | 31 | } | ... | ... |
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,12 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.rule.engine.api; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import org.thingsboard.server.common.data.id.EntityId; | |
18 | 20 | import org.thingsboard.server.common.data.id.RuleNodeId; |
21 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
22 | +import org.thingsboard.server.common.data.kv.KvEntry; | |
23 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
19 | 24 | import org.thingsboard.server.common.msg.TbMsg; |
20 | 25 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
21 | 26 | import org.thingsboard.server.dao.alarm.AlarmService; |
... | ... | @@ -30,6 +35,8 @@ import org.thingsboard.server.dao.rule.RuleService; |
30 | 35 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
31 | 36 | import org.thingsboard.server.dao.user.UserService; |
32 | 37 | |
38 | +import java.util.List; | |
39 | +import java.util.Map; | |
33 | 40 | import java.util.Set; |
34 | 41 | import java.util.UUID; |
35 | 42 | |
... | ... | @@ -56,6 +63,12 @@ public interface TbContext { |
56 | 63 | |
57 | 64 | void tellError(TbMsg msg, Throwable th); |
58 | 65 | |
66 | + void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback); | |
67 | + | |
68 | + void saveAndNotify(EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback); | |
69 | + | |
70 | + void saveAndNotify(EntityId entityId, String scope, Set<AttributeKvEntry> attributes, FutureCallback<Void> callback); | |
71 | + | |
59 | 72 | RuleNodeId getSelfId(); |
60 | 73 | |
61 | 74 | AttributesService getAttributesService(); | ... | ... |