Commit c3ee6785aa381ca130a9423c8f928df983d36227
Merge branch 'master' into feature/log-telemetry-updated
Showing
100 changed files
with
2069 additions
and
636 deletions
Too many changes to show.
To preserve performance only 100 of 243 files are displayed.
... | ... | @@ -391,16 +391,16 @@ |
391 | 391 | }, |
392 | 392 | { |
393 | 393 | "alias": "web_camera_input", |
394 | - "name": "Web Camera Input", | |
394 | + "name": "Photo camera input", | |
395 | 395 | "descriptor": { |
396 | 396 | "type": "latest", |
397 | 397 | "sizeX": 7.5, |
398 | 398 | "sizeY": 3, |
399 | 399 | "resources": [], |
400 | - "templateHtml": "<tb-web-camera-widget \n [ctx]=\"ctx\">\n</tb-web-camera-widget>", | |
400 | + "templateHtml": "<tb-photo-camera-widget \n [ctx]=\"ctx\">\n</tb-photo-camera-widget>", | |
401 | 401 | "templateCss": "", |
402 | - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.webCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", | |
403 | - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Web Camera\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"imageFormat\": {\n \"title\": \"Image Format\",\n \"type\": \"string\",\n \"default\": \"image/png\"\n },\n \"imageQuality\":{\n \"title\":\"Image quality that use lossy compression such as jpeg and webp\",\n \"type\":\"number\",\n \"default\": 0.92,\n \"min\": 0,\n \"max\": 1\n },\n \"maxWidth\": {\n \"title\": \"The maximal image width\",\n \"type\": \"number\",\n \"default\": 640\n }, \n \"maxHeight\": {\n \"title\": \"The maximal image heigth\",\n \"type\": \"number\",\n \"default\": 480\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n {\n \"key\": \"imageFormat\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"image/jpeg\",\n \"label\": \"JPEG\"\n },\n {\n \"value\": \"image/png\",\n \"label\": \"PNG\"\n },\n {\n \"value\": \"image/webp\",\n \"label\": \"WEBP\"\n }\n ]\n },\n \"imageQuality\",\n \"maxWidth\",\n \"maxHeight\"\n ]\n}", | |
402 | + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.photoCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", | |
403 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Photo Camera\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"imageFormat\": {\n \"title\": \"Image Format\",\n \"type\": \"string\",\n \"default\": \"image/png\"\n },\n \"imageQuality\":{\n \"title\":\"Image quality that use lossy compression such as jpeg and webp\",\n \"type\":\"number\",\n \"default\": 0.92,\n \"min\": 0,\n \"max\": 1\n },\n \"maxWidth\": {\n \"title\": \"The maximal image width\",\n \"type\": \"number\",\n \"default\": 640\n }, \n \"maxHeight\": {\n \"title\": \"The maximal image heigth\",\n \"type\": \"number\",\n \"default\": 480\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n {\n \"key\": \"imageFormat\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"image/jpeg\",\n \"label\": \"JPEG\"\n },\n {\n \"value\": \"image/png\",\n \"label\": \"PNG\"\n },\n {\n \"value\": \"image/webp\",\n \"label\": \"WEBP\"\n }\n ]\n },\n \"imageQuality\",\n \"maxWidth\",\n \"maxHeight\"\n ]\n}", | |
404 | 404 | "dataKeySettingsSchema": "{}\n", |
405 | 405 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Web Camera Input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
406 | 406 | } | ... | ... |
... | ... | @@ -96,6 +96,7 @@ CREATE TABLE IF NOT EXISTS device_profile ( |
96 | 96 | is_default boolean, |
97 | 97 | tenant_id uuid, |
98 | 98 | default_rule_chain_id uuid, |
99 | + default_queue_name varchar(255), | |
99 | 100 | provision_device_key varchar, |
100 | 101 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
101 | 102 | CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), | ... | ... |
... | ... | @@ -32,7 +32,6 @@ import org.springframework.data.redis.core.RedisTemplate; |
32 | 32 | import org.springframework.scheduling.annotation.Scheduled; |
33 | 33 | import org.springframework.stereotype.Component; |
34 | 34 | import org.thingsboard.rule.engine.api.MailService; |
35 | -import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; | |
36 | 35 | import org.thingsboard.server.actors.service.ActorService; |
37 | 36 | import org.thingsboard.server.actors.tenant.DebugTbRateLimits; |
38 | 37 | import org.thingsboard.server.common.data.DataConstants; |
... | ... | @@ -65,6 +64,8 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; |
65 | 64 | import org.thingsboard.server.dao.user.UserService; |
66 | 65 | import org.thingsboard.server.queue.discovery.PartitionService; |
67 | 66 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
67 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
68 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
68 | 69 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
69 | 70 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
70 | 71 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
... | ... | @@ -72,7 +73,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; |
72 | 73 | import org.thingsboard.server.service.executors.SharedEventLoopGroupService; |
73 | 74 | import org.thingsboard.server.service.mail.MailExecutorService; |
74 | 75 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
75 | -import org.thingsboard.server.service.profile.TbTenantProfileCache; | |
76 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
76 | 77 | import org.thingsboard.server.service.queue.TbClusterService; |
77 | 78 | import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; |
78 | 79 | import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; |
... | ... | @@ -108,6 +109,14 @@ public class ActorSystemContext { |
108 | 109 | |
109 | 110 | @Autowired |
110 | 111 | @Getter |
112 | + private TbApiUsageStateService apiUsageStateService; | |
113 | + | |
114 | + @Autowired | |
115 | + @Getter | |
116 | + private TbApiUsageClient apiUsageClient; | |
117 | + | |
118 | + @Autowired | |
119 | + @Getter | |
111 | 120 | @Setter |
112 | 121 | private TbServiceInfoProvider serviceInfoProvider; |
113 | 122 | ... | ... |
... | ... | @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.Tenant; |
30 | 30 | import org.thingsboard.server.common.data.TenantProfile; |
31 | 31 | import org.thingsboard.server.common.data.id.EntityId; |
32 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
33 | -import org.thingsboard.server.common.data.id.TenantProfileId; | |
34 | 33 | import org.thingsboard.server.common.data.page.PageDataIterable; |
35 | 34 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
36 | 35 | import org.thingsboard.server.common.msg.MsgType; |
... | ... | @@ -40,10 +39,8 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
40 | 39 | import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; |
41 | 40 | import org.thingsboard.server.common.msg.queue.RuleEngineException; |
42 | 41 | import org.thingsboard.server.common.msg.queue.ServiceType; |
43 | -import org.thingsboard.server.dao.model.ModelConstants; | |
44 | -import org.thingsboard.server.dao.tenant.TenantProfileService; | |
45 | 42 | import org.thingsboard.server.dao.tenant.TenantService; |
46 | -import org.thingsboard.server.service.profile.TbTenantProfileCache; | |
43 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
47 | 44 | import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; |
48 | 45 | |
49 | 46 | import java.util.HashSet; |
... | ... | @@ -150,15 +147,12 @@ public class AppActor extends ContextAwareActor { |
150 | 147 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
151 | 148 | TbActorRef target = null; |
152 | 149 | if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { |
153 | - if (msg.getEntityId().getEntityType() == EntityType.TENANT_PROFILE) { | |
154 | - tenantProfileCache.evict(new TenantProfileId(msg.getEntityId().getId())); | |
155 | - } else { | |
150 | + if (!EntityType.TENANT_PROFILE.equals(msg.getEntityId().getEntityType())) { | |
156 | 151 | log.warn("Message has system tenant id: {}", msg); |
157 | 152 | } |
158 | 153 | } else { |
159 | - if (msg.getEntityId().getEntityType() == EntityType.TENANT) { | |
154 | + if (EntityType.TENANT.equals(msg.getEntityId().getEntityType())) { | |
160 | 155 | TenantId tenantId = new TenantId(msg.getEntityId().getId()); |
161 | - tenantProfileCache.evict(tenantId); | |
162 | 156 | if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { |
163 | 157 | log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); |
164 | 158 | deletedTenants.add(tenantId); | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.Device; |
38 | 38 | import org.thingsboard.server.common.data.DeviceProfile; |
39 | 39 | import org.thingsboard.server.common.data.alarm.Alarm; |
40 | 40 | import org.thingsboard.server.common.data.asset.Asset; |
41 | +import org.thingsboard.server.common.data.id.DeviceId; | |
41 | 42 | import org.thingsboard.server.common.data.id.EntityId; |
42 | 43 | import org.thingsboard.server.common.data.id.RuleChainId; |
43 | 44 | import org.thingsboard.server.common.data.id.RuleNodeId; |
... | ... | @@ -73,6 +74,7 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
73 | 74 | |
74 | 75 | import java.util.Collections; |
75 | 76 | import java.util.Set; |
77 | +import java.util.function.BiConsumer; | |
76 | 78 | import java.util.function.Consumer; |
77 | 79 | |
78 | 80 | /** |
... | ... | @@ -257,26 +259,26 @@ class DefaultTbContext implements TbContext { |
257 | 259 | } |
258 | 260 | |
259 | 261 | public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { |
260 | - return entityCreatedMsg(customer, customer.getId(), ruleNodeId); | |
262 | + return entityActionMsg(customer, customer.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); | |
261 | 263 | } |
262 | 264 | |
263 | 265 | public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { |
264 | - return entityCreatedMsg(device, device.getId(), ruleNodeId); | |
266 | + return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); | |
265 | 267 | } |
266 | 268 | |
267 | 269 | public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { |
268 | - return entityCreatedMsg(asset, asset.getId(), ruleNodeId); | |
270 | + return entityActionMsg(asset, asset.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); | |
269 | 271 | } |
270 | 272 | |
271 | - public TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { | |
272 | - return entityCreatedMsg(alarm, alarm.getId(), ruleNodeId); | |
273 | + public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { | |
274 | + return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action); | |
273 | 275 | } |
274 | 276 | |
275 | - public <E, I extends EntityId> TbMsg entityCreatedMsg(E entity, I id, RuleNodeId ruleNodeId) { | |
277 | + public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { | |
276 | 278 | try { |
277 | - return TbMsg.newMsg(DataConstants.ENTITY_CREATED, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); | |
279 | + return TbMsg.newMsg(action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); | |
278 | 280 | } catch (JsonProcessingException | IllegalArgumentException e) { |
279 | - throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " created msg: " + e); | |
281 | + throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); | |
280 | 282 | } |
281 | 283 | } |
282 | 284 | |
... | ... | @@ -312,7 +314,7 @@ class DefaultTbContext implements TbContext { |
312 | 314 | |
313 | 315 | @Override |
314 | 316 | public ScriptEngine createJsScriptEngine(String script, String... argNames) { |
315 | - return new RuleNodeJsScriptEngine(mainCtx.getJsSandbox(), nodeCtx.getSelf().getId(), script, argNames); | |
317 | + return new RuleNodeJsScriptEngine(getTenantId(), mainCtx.getJsSandbox(), nodeCtx.getSelf().getId(), script, argNames); | |
316 | 318 | } |
317 | 319 | |
318 | 320 | @Override |
... | ... | @@ -479,8 +481,16 @@ class DefaultTbContext implements TbContext { |
479 | 481 | } |
480 | 482 | |
481 | 483 | @Override |
482 | - public void addProfileListener(Consumer<DeviceProfile> listener) { | |
483 | - mainCtx.getDeviceProfileCache().addListener(getTenantId(), getSelfId(), listener); | |
484 | + public void removeRuleNodeStateForEntity(EntityId entityId) { | |
485 | + if (log.isDebugEnabled()) { | |
486 | + log.debug("[{}][{}][{}] Remove Rule Node State for entity.", getTenantId(), getSelfId(), entityId); | |
487 | + } | |
488 | + mainCtx.getRuleNodeStateService().removeByRuleNodeIdAndEntityId(getTenantId(), getSelfId(), entityId); | |
489 | + } | |
490 | + | |
491 | + @Override | |
492 | + public void addDeviceProfileListeners(Consumer<DeviceProfile> profileListener, BiConsumer<DeviceId, DeviceProfile> deviceListener) { | |
493 | + mainCtx.getDeviceProfileCache().addListener(getTenantId(), getSelfId(), profileListener, deviceListener); | |
484 | 494 | } |
485 | 495 | |
486 | 496 | @Override | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import org.thingsboard.server.actors.TbActorRef; |
23 | 23 | import org.thingsboard.server.actors.TbEntityActorId; |
24 | 24 | import org.thingsboard.server.actors.service.DefaultActorService; |
25 | 25 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; |
26 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
26 | 27 | import org.thingsboard.server.common.data.EntityType; |
27 | 28 | import org.thingsboard.server.common.data.id.EntityId; |
28 | 29 | import org.thingsboard.server.common.data.id.RuleChainId; |
... | ... | @@ -46,6 +47,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
46 | 47 | import org.thingsboard.server.queue.TbQueueCallback; |
47 | 48 | import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper; |
48 | 49 | import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper; |
50 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
49 | 51 | import org.thingsboard.server.service.queue.TbClusterService; |
50 | 52 | |
51 | 53 | import java.util.ArrayList; |
... | ... | @@ -68,15 +70,16 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh |
68 | 70 | private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; |
69 | 71 | private final RuleChainService service; |
70 | 72 | private final TbClusterService clusterService; |
73 | + private final TbApiUsageClient apiUsageClient; | |
71 | 74 | private String ruleChainName; |
72 | 75 | |
73 | 76 | private RuleNodeId firstId; |
74 | 77 | private RuleNodeCtx firstNode; |
75 | 78 | private boolean started; |
76 | 79 | |
77 | - RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext | |
78 | - , TbActorRef parent, TbActorRef self) { | |
80 | + RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext, TbActorRef parent, TbActorRef self) { | |
79 | 81 | super(systemContext, tenantId, ruleChain.getId()); |
82 | + this.apiUsageClient = systemContext.getApiUsageClient(); | |
80 | 83 | this.ruleChainName = ruleChain.getName(); |
81 | 84 | this.parent = parent; |
82 | 85 | this.self = self; | ... | ... |
... | ... | @@ -64,6 +64,12 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { |
64 | 64 | } |
65 | 65 | } |
66 | 66 | |
67 | + protected void destroyRuleChains() { | |
68 | + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { | |
69 | + ctx.stop(new TbEntityActorId(ruleChain.getId())); | |
70 | + } | |
71 | + } | |
72 | + | |
67 | 73 | protected void visit(RuleChain entity, TbActorRef actorRef) { |
68 | 74 | if (entity != null && entity.isRoot()) { |
69 | 75 | rootChain = entity; | ... | ... |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
... | ... | @@ -21,13 +21,18 @@ import org.thingsboard.server.actors.ActorSystemContext; |
21 | 21 | import org.thingsboard.server.actors.TbActorCtx; |
22 | 22 | import org.thingsboard.server.actors.TbActorRef; |
23 | 23 | import org.thingsboard.server.actors.shared.ComponentMsgProcessor; |
24 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
25 | +import org.thingsboard.server.common.data.TenantProfile; | |
24 | 26 | import org.thingsboard.server.common.data.id.RuleNodeId; |
25 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
26 | 28 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
27 | 29 | import org.thingsboard.server.common.data.rule.RuleNode; |
30 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; | |
31 | +import org.thingsboard.server.common.msg.TbMsg; | |
28 | 32 | import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; |
29 | 33 | import org.thingsboard.server.common.msg.queue.RuleNodeException; |
30 | 34 | import org.thingsboard.server.common.msg.queue.RuleNodeInfo; |
35 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
31 | 36 | |
32 | 37 | /** |
33 | 38 | * @author Andrew Shvayka |
... | ... | @@ -36,6 +41,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod |
36 | 41 | |
37 | 42 | private final String ruleChainName; |
38 | 43 | private final TbActorRef self; |
44 | + private final TbApiUsageClient apiUsageClient; | |
39 | 45 | private RuleNode ruleNode; |
40 | 46 | private TbNode tbNode; |
41 | 47 | private DefaultTbContext defaultCtx; |
... | ... | @@ -44,6 +50,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod |
44 | 50 | RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName, RuleNodeId ruleNodeId, ActorSystemContext systemContext |
45 | 51 | , TbActorRef parent, TbActorRef self) { |
46 | 52 | super(systemContext, tenantId, ruleNodeId); |
53 | + this.apiUsageClient = systemContext.getApiUsageClient(); | |
47 | 54 | this.ruleChainName = ruleChainName; |
48 | 55 | this.self = self; |
49 | 56 | this.ruleNode = systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId); |
... | ... | @@ -92,26 +99,42 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod |
92 | 99 | |
93 | 100 | public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception { |
94 | 101 | checkActive(msg.getMsg()); |
95 | - if (ruleNode.isDebugMode()) { | |
96 | - systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self"); | |
97 | - } | |
98 | - try { | |
99 | - tbNode.onMsg(defaultCtx, msg.getMsg()); | |
100 | - } catch (Exception e) { | |
101 | - defaultCtx.tellFailure(msg.getMsg(), e); | |
102 | + TbMsg tbMsg = msg.getMsg(); | |
103 | + int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); | |
104 | + int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); | |
105 | + if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { | |
106 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); | |
107 | + if (ruleNode.isDebugMode()) { | |
108 | + systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), "Self"); | |
109 | + } | |
110 | + try { | |
111 | + tbNode.onMsg(defaultCtx, msg.getMsg()); | |
112 | + } catch (Exception e) { | |
113 | + defaultCtx.tellFailure(msg.getMsg(), e); | |
114 | + } | |
115 | + } else { | |
116 | + tbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode)); | |
102 | 117 | } |
103 | 118 | } |
104 | 119 | |
105 | 120 | void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception { |
106 | 121 | msg.getMsg().getCallback().onProcessingStart(info); |
107 | 122 | checkActive(msg.getMsg()); |
108 | - if (ruleNode.isDebugMode()) { | |
109 | - systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); | |
110 | - } | |
111 | - try { | |
112 | - tbNode.onMsg(msg.getCtx(), msg.getMsg()); | |
113 | - } catch (Exception e) { | |
114 | - msg.getCtx().tellFailure(msg.getMsg(), e); | |
123 | + TbMsg tbMsg = msg.getMsg(); | |
124 | + int ruleNodeCount = tbMsg.getAndIncrementRuleNodeCounter(); | |
125 | + int maxRuleNodeExecutionsPerMessage = getTenantProfileConfiguration().getMaxRuleNodeExecsPerMessage(); | |
126 | + if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) { | |
127 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.RE_EXEC_COUNT); | |
128 | + if (ruleNode.isDebugMode()) { | |
129 | + systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); | |
130 | + } | |
131 | + try { | |
132 | + tbNode.onMsg(msg.getCtx(), msg.getMsg()); | |
133 | + } catch (Exception e) { | |
134 | + msg.getCtx().tellFailure(msg.getMsg(), e); | |
135 | + } | |
136 | + } else { | |
137 | + tbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode)); | |
115 | 138 | } |
116 | 139 | } |
117 | 140 | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.beans.factory.annotation.Value; |
21 | 21 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
22 | 22 | import org.springframework.context.event.EventListener; |
23 | +import org.springframework.core.annotation.Order; | |
23 | 24 | import org.springframework.stereotype.Service; |
24 | 25 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
25 | 26 | import org.thingsboard.server.actors.ActorSystemContext; |
... | ... | @@ -113,6 +114,7 @@ public class DefaultActorService implements ActorService { |
113 | 114 | } |
114 | 115 | |
115 | 116 | @EventListener(ApplicationReadyEvent.class) |
117 | + @Order(value = 2) | |
116 | 118 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { |
117 | 119 | log.info("Received application ready event. Sending application init message to actor system"); |
118 | 120 | appActor.tellWithHighPriority(new AppInitMsg()); | ... | ... |
... | ... | @@ -19,9 +19,11 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.thingsboard.server.actors.ActorSystemContext; |
20 | 20 | import org.thingsboard.server.actors.TbActorCtx; |
21 | 21 | import org.thingsboard.server.actors.stats.StatsPersistTick; |
22 | +import org.thingsboard.server.common.data.TenantProfile; | |
22 | 23 | import org.thingsboard.server.common.data.id.EntityId; |
23 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 25 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
26 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; | |
25 | 27 | import org.thingsboard.server.common.msg.TbMsg; |
26 | 28 | import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; |
27 | 29 | import org.thingsboard.server.common.msg.queue.RuleNodeException; |
... | ... | @@ -39,6 +41,10 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract |
39 | 41 | this.entityId = id; |
40 | 42 | } |
41 | 43 | |
44 | + protected TenantProfileConfiguration getTenantProfileConfiguration() { | |
45 | + return systemContext.getTenantProfileCache().get(tenantId).getProfileData().getConfiguration(); | |
46 | + } | |
47 | + | |
42 | 48 | public abstract String getComponentName(); |
43 | 49 | |
44 | 50 | public abstract void start(TbActorCtx context) throws Exception; | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import org.thingsboard.server.actors.device.DeviceActorCreator; |
29 | 29 | import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor; |
30 | 30 | import org.thingsboard.server.actors.service.ContextBasedCreator; |
31 | 31 | import org.thingsboard.server.actors.service.DefaultActorService; |
32 | +import org.thingsboard.server.common.data.ApiUsageState; | |
32 | 33 | import org.thingsboard.server.common.data.EntityType; |
33 | 34 | import org.thingsboard.server.common.data.Tenant; |
34 | 35 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -57,6 +58,7 @@ public class TenantActor extends RuleChainManagerActor { |
57 | 58 | |
58 | 59 | private boolean isRuleEngineForCurrentTenant; |
59 | 60 | private boolean isCore; |
61 | + private ApiUsageState apiUsageState; | |
60 | 62 | |
61 | 63 | private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { |
62 | 64 | super(systemContext, tenantId); |
... | ... | @@ -74,19 +76,24 @@ public class TenantActor extends RuleChainManagerActor { |
74 | 76 | cantFindTenant = true; |
75 | 77 | log.info("[{}] Started tenant actor for missing tenant.", tenantId); |
76 | 78 | } else { |
79 | + apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenant.getId())); | |
80 | + | |
77 | 81 | // This Service may be started for specific tenant only. |
78 | 82 | Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant(); |
79 | 83 | |
80 | 84 | TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId()); |
81 | 85 | |
82 | - isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); | |
83 | 86 | isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); |
84 | - | |
87 | + isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); | |
85 | 88 | if (isRuleEngineForCurrentTenant) { |
86 | 89 | try { |
87 | 90 | if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) { |
88 | - log.info("[{}] Going to init rule chains", tenantId); | |
89 | - initRuleChains(); | |
91 | + if (apiUsageState.isReExecEnabled()) { | |
92 | + log.info("[{}] Going to init rule chains", tenantId); | |
93 | + initRuleChains(); | |
94 | + } else { | |
95 | + log.info("[{}] Skip init of the rule chains due to API limits", tenantId); | |
96 | + } | |
90 | 97 | } else { |
91 | 98 | isRuleEngineForCurrentTenant = false; |
92 | 99 | } |
... | ... | @@ -98,8 +105,6 @@ public class TenantActor extends RuleChainManagerActor { |
98 | 105 | } |
99 | 106 | } catch (Exception e) { |
100 | 107 | log.warn("[{}] Unknown failure", tenantId, e); |
101 | -// TODO: throw this in 3.1? | |
102 | -// throw new TbActorException("Failed to init actor", e); | |
103 | 108 | } |
104 | 109 | } |
105 | 110 | |
... | ... | @@ -115,7 +120,7 @@ public class TenantActor extends RuleChainManagerActor { |
115 | 120 | if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) { |
116 | 121 | QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg; |
117 | 122 | queueMsg.getTbMsg().getCallback().onSuccess(); |
118 | - } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)){ | |
123 | + } else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)) { | |
119 | 124 | TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg; |
120 | 125 | transportMsg.getCallback().onSuccess(); |
121 | 126 | } |
... | ... | @@ -173,26 +178,33 @@ public class TenantActor extends RuleChainManagerActor { |
173 | 178 | return; |
174 | 179 | } |
175 | 180 | TbMsg tbMsg = msg.getTbMsg(); |
176 | - if (tbMsg.getRuleChainId() == null) { | |
177 | - if (getRootChainActor() != null) { | |
178 | - getRootChainActor().tell(msg); | |
181 | + if (apiUsageState.isReExecEnabled()) { | |
182 | + if (tbMsg.getRuleChainId() == null) { | |
183 | + if (getRootChainActor() != null) { | |
184 | + getRootChainActor().tell(msg); | |
185 | + } else { | |
186 | + tbMsg.getCallback().onFailure(new RuleEngineException("No Root Rule Chain available!")); | |
187 | + log.info("[{}] No Root Chain: {}", tenantId, msg); | |
188 | + } | |
179 | 189 | } else { |
180 | - tbMsg.getCallback().onFailure(new RuleEngineException("No Root Rule Chain available!")); | |
181 | - log.info("[{}] No Root Chain: {}", tenantId, msg); | |
190 | + try { | |
191 | + ctx.tell(new TbEntityActorId(tbMsg.getRuleChainId()), msg); | |
192 | + } catch (TbActorNotRegisteredException ex) { | |
193 | + log.trace("Received message for non-existing rule chain: [{}]", tbMsg.getRuleChainId()); | |
194 | + //TODO: 3.1 Log it to dead letters queue; | |
195 | + tbMsg.getCallback().onSuccess(); | |
196 | + } | |
182 | 197 | } |
183 | 198 | } else { |
184 | - try { | |
185 | - ctx.tell(new TbEntityActorId(tbMsg.getRuleChainId()), msg); | |
186 | - } catch (TbActorNotRegisteredException ex) { | |
187 | - log.trace("Received message for non-existing rule chain: [{}]", tbMsg.getRuleChainId()); | |
188 | - //TODO: 3.1 Log it to dead letters queue; | |
189 | - tbMsg.getCallback().onSuccess(); | |
190 | - } | |
199 | + log.trace("[{}] Ack message because Rule Engine is disabled", tenantId); | |
200 | + tbMsg.getCallback().onSuccess(); | |
191 | 201 | } |
192 | 202 | } |
193 | 203 | |
194 | 204 | private void onRuleChainMsg(RuleChainAwareMsg msg) { |
195 | - getOrCreateActor(msg.getRuleChainId()).tell(msg); | |
205 | + if (apiUsageState.isReExecEnabled()) { | |
206 | + getOrCreateActor(msg.getRuleChainId()).tell(msg); | |
207 | + } | |
196 | 208 | } |
197 | 209 | |
198 | 210 | private void onToDeviceActorMsg(DeviceAwareMsg msg, boolean priority) { |
... | ... | @@ -208,6 +220,17 @@ public class TenantActor extends RuleChainManagerActor { |
208 | 220 | } |
209 | 221 | |
210 | 222 | private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { |
223 | + if (msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { | |
224 | + ApiUsageState old = apiUsageState; | |
225 | + apiUsageState = new ApiUsageState(systemContext.getApiUsageStateService().getApiUsageState(tenantId)); | |
226 | + if (old.isReExecEnabled() && !apiUsageState.isReExecEnabled()) { | |
227 | + log.info("[{}] Received API state update. Going to DISABLE Rule Engine execution.", tenantId); | |
228 | + destroyRuleChains(); | |
229 | + } else if (!old.isReExecEnabled() && apiUsageState.isReExecEnabled()) { | |
230 | + log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId); | |
231 | + initRuleChains(); | |
232 | + } | |
233 | + } | |
211 | 234 | if (isRuleEngineForCurrentTenant) { |
212 | 235 | TbActorRef target = getEntityActorRef(msg.getEntityId()); |
213 | 236 | if (target != null) { | ... | ... |
... | ... | @@ -126,6 +126,7 @@ public class AlarmController extends BaseController { |
126 | 126 | long ackTs = System.currentTimeMillis(); |
127 | 127 | alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); |
128 | 128 | alarm.setAckTs(ackTs); |
129 | + alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK); | |
129 | 130 | logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); |
130 | 131 | } catch (Exception e) { |
131 | 132 | throw handleException(e); |
... | ... | @@ -143,6 +144,7 @@ public class AlarmController extends BaseController { |
143 | 144 | long clearTs = System.currentTimeMillis(); |
144 | 145 | alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); |
145 | 146 | alarm.setClearTs(clearTs); |
147 | + alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); | |
146 | 148 | logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); |
147 | 149 | } catch (Exception e) { |
148 | 150 | throw handleException(e); | ... | ... |
... | ... | @@ -166,7 +166,8 @@ public class AuthController extends BaseController { |
166 | 166 | try { |
167 | 167 | String email = resetPasswordByEmailRequest.get("email").asText(); |
168 | 168 | UserCredentials userCredentials = userService.requestPasswordReset(TenantId.SYS_TENANT_ID, email); |
169 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
169 | + User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); | |
170 | + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); | |
170 | 171 | String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl, |
171 | 172 | userCredentials.getResetToken()); |
172 | 173 | |
... | ... | @@ -214,7 +215,7 @@ public class AuthController extends BaseController { |
214 | 215 | User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); |
215 | 216 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); |
216 | 217 | SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); |
217 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
218 | + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); | |
218 | 219 | String loginUrl = String.format("%s/login", baseUrl); |
219 | 220 | String email = user.getEmail(); |
220 | 221 | |
... | ... | @@ -261,7 +262,7 @@ public class AuthController extends BaseController { |
261 | 262 | User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId()); |
262 | 263 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); |
263 | 264 | SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal); |
264 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
265 | + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); | |
265 | 266 | String loginUrl = String.format("%s/login", baseUrl); |
266 | 267 | String email = user.getEmail(); |
267 | 268 | mailService.sendPasswordWasResetEmail(loginUrl, email); | ... | ... |
... | ... | @@ -95,7 +95,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
95 | 95 | import org.thingsboard.server.queue.util.TbCoreComponent; |
96 | 96 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
97 | 97 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
98 | -import org.thingsboard.server.service.profile.TbTenantProfileCache; | |
98 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
99 | 99 | import org.thingsboard.server.service.queue.TbClusterService; |
100 | 100 | import org.thingsboard.server.service.security.model.SecurityUser; |
101 | 101 | import org.thingsboard.server.service.security.permission.AccessControlService; | ... | ... |
... | ... | @@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; |
51 | 51 | import org.thingsboard.server.common.data.id.TenantId; |
52 | 52 | import org.thingsboard.server.common.data.page.PageData; |
53 | 53 | import org.thingsboard.server.common.data.page.PageLink; |
54 | +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | |
54 | 55 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
55 | 56 | import org.thingsboard.server.common.msg.TbMsg; |
56 | 57 | import org.thingsboard.server.common.msg.TbMsgDataType; |
... | ... | @@ -119,6 +120,8 @@ public class DeviceController extends BaseController { |
119 | 120 | |
120 | 121 | tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), |
121 | 122 | savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); |
123 | + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), | |
124 | + device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
122 | 125 | |
123 | 126 | logEntityAction(savedDevice.getId(), savedDevice, |
124 | 127 | savedDevice.getCustomerId(), | ... | ... |
... | ... | @@ -160,7 +160,7 @@ public class RuleChainController extends BaseController { |
160 | 160 | public RuleChain saveRuleChain(@RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException { |
161 | 161 | try { |
162 | 162 | checkNotNull(request); |
163 | - checkNotNull(request.getName()); | |
163 | + checkParameter(request.getName(), "name"); | |
164 | 164 | |
165 | 165 | RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); |
166 | 166 | |
... | ... | @@ -347,7 +347,7 @@ public class RuleChainController extends BaseController { |
347 | 347 | String errorText = ""; |
348 | 348 | ScriptEngine engine = null; |
349 | 349 | try { |
350 | - engine = new RuleNodeJsScriptEngine(jsInvokeService, getCurrentUser().getId(), script, argNames); | |
350 | + engine = new RuleNodeJsScriptEngine(getTenantId(), jsInvokeService, getCurrentUser().getId(), script, argNames); | |
351 | 351 | TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); |
352 | 352 | switch (scriptType) { |
353 | 353 | case "update": | ... | ... |
... | ... | @@ -93,6 +93,8 @@ public class TenantController extends BaseController { |
93 | 93 | } |
94 | 94 | tenantProfileCache.evict(tenant.getId()); |
95 | 95 | tbClusterService.onTenantChange(tenant, null); |
96 | + tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(), | |
97 | + newTenant ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
96 | 98 | return tenant; |
97 | 99 | } catch (Exception e) { |
98 | 100 | throw handleException(e); | ... | ... |
... | ... | @@ -52,6 +52,7 @@ import org.thingsboard.server.service.security.model.token.JwtToken; |
52 | 52 | import org.thingsboard.server.service.security.model.token.JwtTokenFactory; |
53 | 53 | import org.thingsboard.server.service.security.permission.Operation; |
54 | 54 | import org.thingsboard.server.service.security.permission.Resource; |
55 | +import org.thingsboard.server.service.security.system.SystemSecurityService; | |
55 | 56 | import org.thingsboard.server.utils.MiscUtils; |
56 | 57 | |
57 | 58 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -78,6 +79,9 @@ public class UserController extends BaseController { |
78 | 79 | @Autowired |
79 | 80 | private RefreshTokenRepository refreshTokenRepository; |
80 | 81 | |
82 | + @Autowired | |
83 | + private SystemSecurityService systemSecurityService; | |
84 | + | |
81 | 85 | |
82 | 86 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
83 | 87 | @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET) |
... | ... | @@ -146,7 +150,7 @@ public class UserController extends BaseController { |
146 | 150 | if (sendEmail) { |
147 | 151 | SecurityUser authUser = getCurrentUser(); |
148 | 152 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId()); |
149 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
153 | + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); | |
150 | 154 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, |
151 | 155 | userCredentials.getActivateToken()); |
152 | 156 | String email = savedUser.getEmail(); |
... | ... | @@ -186,7 +190,7 @@ public class UserController extends BaseController { |
186 | 190 | |
187 | 191 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); |
188 | 192 | if (!userCredentials.isEnabled()) { |
189 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
193 | + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); | |
190 | 194 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, |
191 | 195 | userCredentials.getActivateToken()); |
192 | 196 | mailService.sendActivationEmail(activateUrl, email); |
... | ... | @@ -211,7 +215,7 @@ public class UserController extends BaseController { |
211 | 215 | SecurityUser authUser = getCurrentUser(); |
212 | 216 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); |
213 | 217 | if (!userCredentials.isEnabled()) { |
214 | - String baseUrl = MiscUtils.constructBaseUrl(request); | |
218 | + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); | |
215 | 219 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, |
216 | 220 | userCredentials.getActivateToken()); |
217 | 221 | return activateUrl; | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.apiusage; | |
17 | + | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.checkerframework.checker.nullness.qual.Nullable; | |
21 | +import org.springframework.beans.factory.annotation.Autowired; | |
22 | +import org.springframework.beans.factory.annotation.Value; | |
23 | +import org.springframework.boot.context.event.ApplicationReadyEvent; | |
24 | +import org.springframework.context.annotation.Lazy; | |
25 | +import org.springframework.context.event.EventListener; | |
26 | +import org.springframework.core.annotation.Order; | |
27 | +import org.springframework.data.util.Pair; | |
28 | +import org.springframework.stereotype.Service; | |
29 | +import org.thingsboard.server.common.data.ApiFeature; | |
30 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
31 | +import org.thingsboard.server.common.data.ApiUsageState; | |
32 | +import org.thingsboard.server.common.data.ApiUsageStateValue; | |
33 | +import org.thingsboard.server.common.data.Tenant; | |
34 | +import org.thingsboard.server.common.data.TenantProfile; | |
35 | +import org.thingsboard.server.common.data.id.ApiUsageStateId; | |
36 | +import org.thingsboard.server.common.data.id.TenantId; | |
37 | +import org.thingsboard.server.common.data.id.TenantProfileId; | |
38 | +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | |
39 | +import org.thingsboard.server.common.data.kv.LongDataEntry; | |
40 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | |
41 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
42 | +import org.thingsboard.server.common.data.page.PageDataIterable; | |
43 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; | |
44 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
45 | +import org.thingsboard.server.common.msg.queue.ServiceType; | |
46 | +import org.thingsboard.server.common.msg.queue.TbCallback; | |
47 | +import org.thingsboard.server.common.msg.tools.SchedulerUtils; | |
48 | +import org.thingsboard.server.dao.tenant.TenantService; | |
49 | +import org.thingsboard.server.dao.timeseries.TimeseriesService; | |
50 | +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | |
51 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
52 | +import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; | |
53 | +import org.thingsboard.server.queue.common.TbProtoQueueMsg; | |
54 | +import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
55 | +import org.thingsboard.server.queue.discovery.PartitionService; | |
56 | +import org.thingsboard.server.queue.scheduler.SchedulerComponent; | |
57 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
58 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
59 | +import org.thingsboard.server.service.queue.TbClusterService; | |
60 | +import org.thingsboard.server.service.telemetry.InternalTelemetryService; | |
61 | + | |
62 | +import javax.annotation.PostConstruct; | |
63 | +import java.util.ArrayList; | |
64 | +import java.util.HashMap; | |
65 | +import java.util.HashSet; | |
66 | +import java.util.List; | |
67 | +import java.util.Map; | |
68 | +import java.util.Set; | |
69 | +import java.util.UUID; | |
70 | +import java.util.concurrent.ConcurrentHashMap; | |
71 | +import java.util.concurrent.ExecutionException; | |
72 | +import java.util.concurrent.TimeUnit; | |
73 | +import java.util.concurrent.locks.Lock; | |
74 | +import java.util.concurrent.locks.ReentrantLock; | |
75 | + | |
76 | +@Slf4j | |
77 | +@Service | |
78 | +public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | |
79 | + | |
80 | + public static final String HOURLY = "Hourly"; | |
81 | + public static final FutureCallback<Integer> VOID_CALLBACK = new FutureCallback<Integer>() { | |
82 | + @Override | |
83 | + public void onSuccess(@Nullable Integer result) { | |
84 | + } | |
85 | + | |
86 | + @Override | |
87 | + public void onFailure(Throwable t) { | |
88 | + } | |
89 | + }; | |
90 | + private final TbClusterService clusterService; | |
91 | + private final PartitionService partitionService; | |
92 | + private final TenantService tenantService; | |
93 | + private final TimeseriesService tsService; | |
94 | + private final ApiUsageStateService apiUsageStateService; | |
95 | + private final SchedulerComponent scheduler; | |
96 | + private final TbTenantProfileCache tenantProfileCache; | |
97 | + | |
98 | + @Lazy | |
99 | + @Autowired | |
100 | + private InternalTelemetryService tsWsService; | |
101 | + | |
102 | + // Tenants that should be processed on this server | |
103 | + private final Map<TenantId, TenantApiUsageState> myTenantStates = new ConcurrentHashMap<>(); | |
104 | + // Tenants that should be processed on other servers | |
105 | + private final Map<TenantId, ApiUsageState> otherTenantStates = new ConcurrentHashMap<>(); | |
106 | + | |
107 | + @Value("${usage.stats.report.enabled:true}") | |
108 | + private boolean enabled; | |
109 | + | |
110 | + @Value("${usage.stats.check.cycle:60000}") | |
111 | + private long nextCycleCheckInterval; | |
112 | + | |
113 | + private final Lock updateLock = new ReentrantLock(); | |
114 | + | |
115 | + public DefaultTbApiUsageStateService(TbClusterService clusterService, | |
116 | + PartitionService partitionService, | |
117 | + TenantService tenantService, | |
118 | + TimeseriesService tsService, | |
119 | + ApiUsageStateService apiUsageStateService, | |
120 | + SchedulerComponent scheduler, | |
121 | + TbTenantProfileCache tenantProfileCache) { | |
122 | + this.clusterService = clusterService; | |
123 | + this.partitionService = partitionService; | |
124 | + this.tenantService = tenantService; | |
125 | + this.tsService = tsService; | |
126 | + this.apiUsageStateService = apiUsageStateService; | |
127 | + this.scheduler = scheduler; | |
128 | + this.tenantProfileCache = tenantProfileCache; | |
129 | + } | |
130 | + | |
131 | + @PostConstruct | |
132 | + public void init() { | |
133 | + if (enabled) { | |
134 | + log.info("Starting api usage service."); | |
135 | + scheduler.scheduleAtFixedRate(this::checkStartOfNextCycle, nextCycleCheckInterval, nextCycleCheckInterval, TimeUnit.MILLISECONDS); | |
136 | + log.info("Started api usage service."); | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + @Override | |
141 | + public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { | |
142 | + ToUsageStatsServiceMsg statsMsg = msg.getValue(); | |
143 | + TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); | |
144 | + TenantApiUsageState tenantState; | |
145 | + List<TsKvEntry> updatedEntries; | |
146 | + Map<ApiFeature, ApiUsageStateValue> result; | |
147 | + updateLock.lock(); | |
148 | + try { | |
149 | + tenantState = getOrFetchState(tenantId); | |
150 | + long ts = tenantState.getCurrentCycleTs(); | |
151 | + long hourTs = tenantState.getCurrentHourTs(); | |
152 | + long newHourTs = SchedulerUtils.getStartOfCurrentHour(); | |
153 | + if (newHourTs != hourTs) { | |
154 | + tenantState.setHour(newHourTs); | |
155 | + } | |
156 | + updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); | |
157 | + Set<ApiFeature> apiFeatures = new HashSet<>(); | |
158 | + for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { | |
159 | + ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); | |
160 | + long newValue = tenantState.add(recordKey, kvProto.getValue()); | |
161 | + updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.getApiCountKey(), newValue))); | |
162 | + long newHourlyValue = tenantState.addToHourly(recordKey, kvProto.getValue()); | |
163 | + updatedEntries.add(new BasicTsKvEntry(hourTs, new LongDataEntry(recordKey.getApiCountKey() + HOURLY, newHourlyValue))); | |
164 | + apiFeatures.add(recordKey.getApiFeature()); | |
165 | + } | |
166 | + result = tenantState.checkStateUpdatedDueToThreshold(apiFeatures); | |
167 | + } finally { | |
168 | + updateLock.unlock(); | |
169 | + } | |
170 | + tsWsService.saveAndNotifyInternal(tenantId, tenantState.getApiUsageState().getId(), updatedEntries, VOID_CALLBACK); | |
171 | + if (!result.isEmpty()) { | |
172 | + persistAndNotify(tenantState, result); | |
173 | + } | |
174 | + callback.onSuccess(); | |
175 | + } | |
176 | + | |
177 | + @Override | |
178 | + public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) { | |
179 | + if (partitionChangeEvent.getServiceType().equals(ServiceType.TB_CORE)) { | |
180 | + myTenantStates.entrySet().removeIf(entry -> !partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | |
181 | + otherTenantStates.entrySet().removeIf(entry -> partitionService.resolve(ServiceType.TB_CORE, entry.getKey(), entry.getKey()).isMyPartition()); | |
182 | + initStatesFromDataBase(); | |
183 | + } | |
184 | + } | |
185 | + | |
186 | + @Override | |
187 | + public ApiUsageState getApiUsageState(TenantId tenantId) { | |
188 | + TenantApiUsageState tenantState = myTenantStates.get(tenantId); | |
189 | + if (tenantState != null) { | |
190 | + return tenantState.getApiUsageState(); | |
191 | + } else { | |
192 | + ApiUsageState state = otherTenantStates.get(tenantId); | |
193 | + if (state != null) { | |
194 | + return state; | |
195 | + } else { | |
196 | + if (partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { | |
197 | + return getOrFetchState(tenantId).getApiUsageState(); | |
198 | + } else { | |
199 | + updateLock.lock(); | |
200 | + try { | |
201 | + state = otherTenantStates.get(tenantId); | |
202 | + if (state == null) { | |
203 | + state = apiUsageStateService.findTenantApiUsageState(tenantId); | |
204 | + if (state != null) { | |
205 | + otherTenantStates.put(tenantId, state); | |
206 | + } | |
207 | + } | |
208 | + } finally { | |
209 | + updateLock.unlock(); | |
210 | + } | |
211 | + return state; | |
212 | + } | |
213 | + } | |
214 | + } | |
215 | + } | |
216 | + | |
217 | + @Override | |
218 | + public void onApiUsageStateUpdate(TenantId tenantId) { | |
219 | + otherTenantStates.remove(tenantId); | |
220 | + } | |
221 | + | |
222 | + @Override | |
223 | + public void onTenantProfileUpdate(TenantProfileId tenantProfileId) { | |
224 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantProfileId); | |
225 | + updateLock.lock(); | |
226 | + try { | |
227 | + myTenantStates.values().forEach(state -> { | |
228 | + if (tenantProfile.getId().equals(state.getTenantProfileId())) { | |
229 | + updateTenantState(state, tenantProfile); | |
230 | + } | |
231 | + }); | |
232 | + } finally { | |
233 | + updateLock.unlock(); | |
234 | + } | |
235 | + } | |
236 | + | |
237 | + @Override | |
238 | + public void onTenantUpdate(TenantId tenantId) { | |
239 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | |
240 | + updateLock.lock(); | |
241 | + try { | |
242 | + TenantApiUsageState state = myTenantStates.get(tenantId); | |
243 | + if (state != null && !state.getTenantProfileId().equals(tenantProfile.getId())) { | |
244 | + updateTenantState(state, tenantProfile); | |
245 | + } | |
246 | + } finally { | |
247 | + updateLock.unlock(); | |
248 | + } | |
249 | + } | |
250 | + | |
251 | + private void updateTenantState(TenantApiUsageState state, TenantProfile tenantProfile) { | |
252 | + TenantProfileData oldProfileData = state.getTenantProfileData(); | |
253 | + state.setTenantProfileId(tenantProfile.getId()); | |
254 | + state.setTenantProfileData(tenantProfile.getProfileData()); | |
255 | + Map<ApiFeature, ApiUsageStateValue> result = state.checkStateUpdatedDueToThresholds(); | |
256 | + if (!result.isEmpty()) { | |
257 | + persistAndNotify(state, result); | |
258 | + } | |
259 | + updateProfileThresholds(state.getTenantId(), state.getApiUsageState().getId(), | |
260 | + oldProfileData.getConfiguration(), tenantProfile.getProfileData().getConfiguration()); | |
261 | + } | |
262 | + | |
263 | + private void updateProfileThresholds(TenantId tenantId, ApiUsageStateId id, | |
264 | + TenantProfileConfiguration oldData, TenantProfileConfiguration newData) { | |
265 | + long ts = System.currentTimeMillis(); | |
266 | + List<TsKvEntry> profileThresholds = new ArrayList<>(); | |
267 | + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | |
268 | + long newProfileThreshold = newData.getProfileThreshold(key); | |
269 | + if (oldData == null || oldData.getProfileThreshold(key) != newProfileThreshold) { | |
270 | + log.info("[{}] Updating profile threshold [{}]:[{}]", tenantId, key, newProfileThreshold); | |
271 | + profileThresholds.add(new BasicTsKvEntry(ts, new LongDataEntry(key.getApiLimitKey(), newProfileThreshold))); | |
272 | + } | |
273 | + } | |
274 | + if (!profileThresholds.isEmpty()) { | |
275 | + tsWsService.saveAndNotifyInternal(tenantId, id, profileThresholds, VOID_CALLBACK); | |
276 | + } | |
277 | + } | |
278 | + | |
279 | + private void persistAndNotify(TenantApiUsageState state, Map<ApiFeature, ApiUsageStateValue> result) { | |
280 | + log.info("[{}] Detected update of the API state: {}", state.getTenantId(), result); | |
281 | + apiUsageStateService.update(state.getApiUsageState()); | |
282 | + clusterService.onApiStateChange(state.getApiUsageState(), null); | |
283 | + long ts = System.currentTimeMillis(); | |
284 | + List<TsKvEntry> stateTelemetry = new ArrayList<>(); | |
285 | + result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); | |
286 | + tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); | |
287 | + //TODO: notify tenant admin via email! | |
288 | + } | |
289 | + | |
290 | + private void checkStartOfNextCycle() { | |
291 | + updateLock.lock(); | |
292 | + try { | |
293 | + long now = System.currentTimeMillis(); | |
294 | + myTenantStates.values().forEach(state -> { | |
295 | + if ((state.getNextCycleTs() > now) && (state.getNextCycleTs() - now < TimeUnit.HOURS.toMillis(1))) { | |
296 | + state.setCycles(state.getNextCycleTs(), SchedulerUtils.getStartOfNextNextMonth()); | |
297 | + } | |
298 | + }); | |
299 | + } finally { | |
300 | + updateLock.unlock(); | |
301 | + } | |
302 | + } | |
303 | + | |
304 | + private TenantApiUsageState getOrFetchState(TenantId tenantId) { | |
305 | + TenantApiUsageState tenantState = myTenantStates.get(tenantId); | |
306 | + if (tenantState == null) { | |
307 | + ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); | |
308 | + if (dbStateEntity == null) { | |
309 | + try { | |
310 | + dbStateEntity = apiUsageStateService.createDefaultApiUsageState(tenantId); | |
311 | + } catch (Exception e) { | |
312 | + dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); | |
313 | + } | |
314 | + } | |
315 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | |
316 | + tenantState = new TenantApiUsageState(tenantProfile, dbStateEntity); | |
317 | + try { | |
318 | + List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getId()).get(); | |
319 | + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | |
320 | + boolean cycleEntryFound = false; | |
321 | + boolean hourlyEntryFound = false; | |
322 | + for (TsKvEntry tsKvEntry : dbValues) { | |
323 | + if (tsKvEntry.getKey().equals(key.getApiCountKey())) { | |
324 | + cycleEntryFound = true; | |
325 | + tenantState.put(key, tsKvEntry.getTs() == tenantState.getCurrentCycleTs() ? tsKvEntry.getLongValue().get() : 0L); | |
326 | + } else if (tsKvEntry.getKey().equals(key.getApiCountKey() + HOURLY)) { | |
327 | + hourlyEntryFound = true; | |
328 | + tenantState.putHourly(key, tsKvEntry.getTs() == tenantState.getCurrentHourTs() ? tsKvEntry.getLongValue().get() : 0L); | |
329 | + } | |
330 | + if (cycleEntryFound && hourlyEntryFound) { | |
331 | + break; | |
332 | + } | |
333 | + } | |
334 | + } | |
335 | + log.debug("[{}] Initialized state: {}", tenantId, dbStateEntity); | |
336 | + myTenantStates.put(tenantId, tenantState); | |
337 | + } catch (InterruptedException | ExecutionException e) { | |
338 | + log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e); | |
339 | + } | |
340 | + } | |
341 | + return tenantState; | |
342 | + } | |
343 | + | |
344 | + private void initStatesFromDataBase() { | |
345 | + try { | |
346 | + log.info("Initializing tenant states."); | |
347 | + PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, 1024); | |
348 | + for (Tenant tenant : tenantIterator) { | |
349 | + if (!myTenantStates.containsKey(tenant.getId()) && partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), tenant.getId()).isMyPartition()) { | |
350 | + log.debug("[{}] Initializing tenant state.", tenant.getId()); | |
351 | + updateLock.lock(); | |
352 | + try { | |
353 | + updateTenantState(getOrFetchState(tenant.getId()), tenantProfileCache.get(tenant.getTenantProfileId())); | |
354 | + log.debug("[{}] Initialized tenant state.", tenant.getId()); | |
355 | + } catch (Exception e) { | |
356 | + log.warn("[{}] Failed to initialize tenant API state", tenant.getId(), e); | |
357 | + } finally { | |
358 | + updateLock.unlock(); | |
359 | + } | |
360 | + } | |
361 | + } | |
362 | + log.info("Initialized tenant states."); | |
363 | + } catch (Exception e) { | |
364 | + log.warn("Unknown failure", e); | |
365 | + } | |
366 | + } | |
367 | + | |
368 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/TbApiUsageStateService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.apiusage; | |
17 | + | |
18 | +import org.springframework.context.ApplicationListener; | |
19 | +import org.thingsboard.server.common.data.ApiUsageState; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.common.data.id.TenantProfileId; | |
22 | +import org.thingsboard.server.common.msg.queue.TbCallback; | |
23 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
24 | +import org.thingsboard.server.queue.common.TbProtoQueueMsg; | |
25 | +import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
26 | + | |
27 | +public interface TbApiUsageStateService extends ApplicationListener<PartitionChangeEvent> { | |
28 | + | |
29 | + void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback); | |
30 | + | |
31 | + ApiUsageState getApiUsageState(TenantId tenantId); | |
32 | + | |
33 | + void onTenantProfileUpdate(TenantProfileId tenantProfileId); | |
34 | + | |
35 | + void onTenantUpdate(TenantId tenantId); | |
36 | + | |
37 | + void onApiUsageStateUpdate(TenantId tenantId); | |
38 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/TenantApiUsageState.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.apiusage; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | +import lombok.Setter; | |
20 | +import org.springframework.data.util.Pair; | |
21 | +import org.thingsboard.server.common.data.ApiFeature; | |
22 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
23 | +import org.thingsboard.server.common.data.ApiUsageState; | |
24 | +import org.thingsboard.server.common.data.ApiUsageStateValue; | |
25 | +import org.thingsboard.server.common.data.TenantProfile; | |
26 | +import org.thingsboard.server.common.data.id.TenantId; | |
27 | +import org.thingsboard.server.common.data.id.TenantProfileId; | |
28 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
29 | +import org.thingsboard.server.common.msg.tools.SchedulerUtils; | |
30 | + | |
31 | +import java.util.Arrays; | |
32 | +import java.util.HashMap; | |
33 | +import java.util.HashSet; | |
34 | +import java.util.Map; | |
35 | +import java.util.Set; | |
36 | +import java.util.concurrent.ConcurrentHashMap; | |
37 | + | |
38 | +public class TenantApiUsageState { | |
39 | + | |
40 | + private final Map<ApiUsageRecordKey, Long> currentCycleValues = new ConcurrentHashMap<>(); | |
41 | + private final Map<ApiUsageRecordKey, Long> currentHourValues = new ConcurrentHashMap<>(); | |
42 | + | |
43 | + @Getter | |
44 | + @Setter | |
45 | + private TenantProfileId tenantProfileId; | |
46 | + @Getter | |
47 | + @Setter | |
48 | + private TenantProfileData tenantProfileData; | |
49 | + @Getter | |
50 | + private final ApiUsageState apiUsageState; | |
51 | + @Getter | |
52 | + private volatile long currentCycleTs; | |
53 | + @Getter | |
54 | + private volatile long nextCycleTs; | |
55 | + @Getter | |
56 | + private volatile long currentHourTs; | |
57 | + | |
58 | + public TenantApiUsageState(TenantProfile tenantProfile, ApiUsageState apiUsageState) { | |
59 | + this.tenantProfileId = tenantProfile.getId(); | |
60 | + this.tenantProfileData = tenantProfile.getProfileData(); | |
61 | + this.apiUsageState = apiUsageState; | |
62 | + this.currentCycleTs = SchedulerUtils.getStartOfCurrentMonth(); | |
63 | + this.nextCycleTs = SchedulerUtils.getStartOfNextMonth(); | |
64 | + this.currentHourTs = SchedulerUtils.getStartOfCurrentHour(); | |
65 | + } | |
66 | + | |
67 | + public void put(ApiUsageRecordKey key, Long value) { | |
68 | + currentCycleValues.put(key, value); | |
69 | + } | |
70 | + | |
71 | + public void putHourly(ApiUsageRecordKey key, Long value) { | |
72 | + currentHourValues.put(key, value); | |
73 | + } | |
74 | + | |
75 | + public long add(ApiUsageRecordKey key, long value) { | |
76 | + long result = currentCycleValues.getOrDefault(key, 0L) + value; | |
77 | + currentCycleValues.put(key, result); | |
78 | + return result; | |
79 | + } | |
80 | + | |
81 | + public long get(ApiUsageRecordKey key) { | |
82 | + return currentCycleValues.getOrDefault(key, 0L); | |
83 | + } | |
84 | + | |
85 | + public long addToHourly(ApiUsageRecordKey key, long value) { | |
86 | + long result = currentHourValues.getOrDefault(key, 0L) + value; | |
87 | + currentHourValues.put(key, result); | |
88 | + return result; | |
89 | + } | |
90 | + | |
91 | + public void setHour(long currentHourTs) { | |
92 | + this.currentHourTs = currentHourTs; | |
93 | + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | |
94 | + currentHourValues.put(key, 0L); | |
95 | + } | |
96 | + } | |
97 | + | |
98 | + public void setCycles(long currentCycleTs, long nextCycleTs) { | |
99 | + this.currentCycleTs = currentCycleTs; | |
100 | + this.nextCycleTs = nextCycleTs; | |
101 | + for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { | |
102 | + currentCycleValues.put(key, 0L); | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + public long getProfileThreshold(ApiUsageRecordKey key) { | |
107 | + return tenantProfileData.getConfiguration().getProfileThreshold(key); | |
108 | + } | |
109 | + | |
110 | + public long getProfileWarnThreshold(ApiUsageRecordKey key) { | |
111 | + return tenantProfileData.getConfiguration().getWarnThreshold(key); | |
112 | + } | |
113 | + | |
114 | + public TenantId getTenantId() { | |
115 | + return apiUsageState.getTenantId(); | |
116 | + } | |
117 | + | |
118 | + public ApiUsageStateValue getFeatureValue(ApiFeature feature) { | |
119 | + switch (feature) { | |
120 | + case TRANSPORT: | |
121 | + return apiUsageState.getTransportState(); | |
122 | + case RE: | |
123 | + return apiUsageState.getReExecState(); | |
124 | + case DB: | |
125 | + return apiUsageState.getDbStorageState(); | |
126 | + case JS: | |
127 | + return apiUsageState.getJsExecState(); | |
128 | + default: | |
129 | + return ApiUsageStateValue.ENABLED; | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + public boolean setFeatureValue(ApiFeature feature, ApiUsageStateValue value) { | |
134 | + ApiUsageStateValue currentValue = getFeatureValue(feature); | |
135 | + switch (feature) { | |
136 | + case TRANSPORT: | |
137 | + apiUsageState.setTransportState(value); | |
138 | + break; | |
139 | + case RE: | |
140 | + apiUsageState.setReExecState(value); | |
141 | + break; | |
142 | + case DB: | |
143 | + apiUsageState.setDbStorageState(value); | |
144 | + break; | |
145 | + case JS: | |
146 | + apiUsageState.setJsExecState(value); | |
147 | + break; | |
148 | + } | |
149 | + return !currentValue.equals(value); | |
150 | + } | |
151 | + | |
152 | + public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThresholds() { | |
153 | + return checkStateUpdatedDueToThreshold(new HashSet<>(Arrays.asList(ApiFeature.values()))); | |
154 | + } | |
155 | + | |
156 | + public Map<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(Set<ApiFeature> features) { | |
157 | + Map<ApiFeature, ApiUsageStateValue> result = new HashMap<>(); | |
158 | + for (ApiFeature feature : features) { | |
159 | + Pair<ApiFeature, ApiUsageStateValue> tmp = checkStateUpdatedDueToThreshold(feature); | |
160 | + if (tmp != null) { | |
161 | + result.put(tmp.getFirst(), tmp.getSecond()); | |
162 | + } | |
163 | + } | |
164 | + return result; | |
165 | + } | |
166 | + | |
167 | + public Pair<ApiFeature, ApiUsageStateValue> checkStateUpdatedDueToThreshold(ApiFeature feature) { | |
168 | + ApiUsageStateValue featureValue = ApiUsageStateValue.ENABLED; | |
169 | + for (ApiUsageRecordKey recordKey : ApiUsageRecordKey.getKeys(feature)) { | |
170 | + long value = get(recordKey); | |
171 | + long threshold = getProfileThreshold(recordKey); | |
172 | + long warnThreshold = getProfileWarnThreshold(recordKey); | |
173 | + ApiUsageStateValue tmpValue; | |
174 | + if (threshold == 0 || value < warnThreshold) { | |
175 | + tmpValue = ApiUsageStateValue.ENABLED; | |
176 | + } else if (value < threshold) { | |
177 | + tmpValue = ApiUsageStateValue.WARNING; | |
178 | + } else { | |
179 | + tmpValue = ApiUsageStateValue.DISABLED; | |
180 | + } | |
181 | + featureValue = ApiUsageStateValue.toMoreRestricted(featureValue, tmpValue); | |
182 | + } | |
183 | + return setFeatureValue(feature, featureValue) ? Pair.of(feature, featureValue) : null; | |
184 | + } | |
185 | + | |
186 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -30,7 +30,8 @@ import org.thingsboard.server.common.data.Device; |
30 | 30 | import org.thingsboard.server.common.data.DeviceProfile; |
31 | 31 | import org.thingsboard.server.common.data.Tenant; |
32 | 32 | import org.thingsboard.server.common.data.TenantProfile; |
33 | -import org.thingsboard.server.common.data.TenantProfileData; | |
33 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
34 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
34 | 35 | import org.thingsboard.server.common.data.User; |
35 | 36 | import org.thingsboard.server.common.data.asset.Asset; |
36 | 37 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -127,6 +128,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
127 | 128 | public void createDefaultTenantProfiles() throws Exception { |
128 | 129 | tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID); |
129 | 130 | |
131 | + TenantProfileData tenantProfileData = new TenantProfileData(); | |
132 | + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); | |
133 | + | |
130 | 134 | TenantProfile isolatedTbCoreProfile = new TenantProfile(); |
131 | 135 | isolatedTbCoreProfile.setDefault(false); |
132 | 136 | isolatedTbCoreProfile.setName("Isolated TB Core"); |
... | ... | @@ -134,6 +138,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
134 | 138 | isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile"); |
135 | 139 | isolatedTbCoreProfile.setIsolatedTbCore(true); |
136 | 140 | isolatedTbCoreProfile.setIsolatedTbRuleEngine(false); |
141 | + isolatedTbCoreProfile.setProfileData(tenantProfileData); | |
137 | 142 | try { |
138 | 143 | tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreProfile); |
139 | 144 | } catch (DataValidationException e) { |
... | ... | @@ -147,6 +152,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
147 | 152 | isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile"); |
148 | 153 | isolatedTbRuleEngineProfile.setIsolatedTbCore(false); |
149 | 154 | isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true); |
155 | + isolatedTbRuleEngineProfile.setProfileData(tenantProfileData); | |
156 | + | |
150 | 157 | try { |
151 | 158 | tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbRuleEngineProfile); |
152 | 159 | } catch (DataValidationException e) { |
... | ... | @@ -160,6 +167,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
160 | 167 | isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile"); |
161 | 168 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true); |
162 | 169 | isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true); |
170 | + isolatedTbCoreAndTbRuleEngineProfile.setProfileData(tenantProfileData); | |
171 | + | |
163 | 172 | try { |
164 | 173 | tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile); |
165 | 174 | } catch (DataValidationException e) { |
... | ... | @@ -173,6 +182,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
173 | 182 | generalSettings.setKey("general"); |
174 | 183 | ObjectNode node = objectMapper.createObjectNode(); |
175 | 184 | node.put("baseUrl", "http://localhost:8080"); |
185 | + node.put("prohibitDifferentUrl", true); | |
176 | 186 | generalSettings.setJsonValue(node); |
177 | 187 | adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); |
178 | 188 | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; |
28 | 28 | import org.thingsboard.server.dao.device.DeviceProfileService; |
29 | 29 | import org.thingsboard.server.dao.device.DeviceService; |
30 | 30 | import org.thingsboard.server.dao.tenant.TenantService; |
31 | +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | |
31 | 32 | import org.thingsboard.server.service.install.sql.SqlDbHelper; |
32 | 33 | |
33 | 34 | import java.nio.charset.Charset; |
... | ... | @@ -96,6 +97,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
96 | 97 | @Autowired |
97 | 98 | private DeviceProfileService deviceProfileService; |
98 | 99 | |
100 | + @Autowired | |
101 | + private ApiUsageStateService apiUsageStateService; | |
102 | + | |
99 | 103 | |
100 | 104 | @Override |
101 | 105 | public void upgradeDatabase(String fromVersion) throws Exception { |
... | ... | @@ -352,6 +356,22 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
352 | 356 | } catch (Exception e) { |
353 | 357 | } |
354 | 358 | |
359 | + try { | |
360 | + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS api_usage_state (" + | |
361 | + " id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY," + | |
362 | + " created_time bigint NOT NULL," + | |
363 | + " tenant_id uuid," + | |
364 | + " entity_type varchar(32)," + | |
365 | + " entity_id uuid," + | |
366 | + " transport varchar(32)," + | |
367 | + " db_storage varchar(32)," + | |
368 | + " re_exec varchar(32)," + | |
369 | + " js_exec varchar(32)," + | |
370 | + " CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" + | |
371 | + ");"); | |
372 | + } catch (Exception e) { | |
373 | + } | |
374 | + | |
355 | 375 | schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql"); |
356 | 376 | loadSql(schemaUpdateFile, conn); |
357 | 377 | |
... | ... | @@ -367,6 +387,10 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
367 | 387 | do { |
368 | 388 | pageData = tenantService.findTenants(pageLink); |
369 | 389 | for (Tenant tenant : pageData.getData()) { |
390 | + try { | |
391 | + apiUsageStateService.createDefaultApiUsageState(tenant.getId()); | |
392 | + } catch (Exception e) { | |
393 | + } | |
370 | 394 | List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); |
371 | 395 | try { |
372 | 396 | deviceProfileService.createDefaultDeviceProfile(tenant.getId()); | ... | ... |
... | ... | @@ -26,11 +26,11 @@ import org.thingsboard.server.common.data.id.TenantId; |
26 | 26 | import org.thingsboard.server.dao.device.DeviceProfileService; |
27 | 27 | import org.thingsboard.server.dao.device.DeviceService; |
28 | 28 | |
29 | -import java.util.Map; | |
30 | 29 | import java.util.concurrent.ConcurrentHashMap; |
31 | 30 | import java.util.concurrent.ConcurrentMap; |
32 | 31 | import java.util.concurrent.locks.Lock; |
33 | 32 | import java.util.concurrent.locks.ReentrantLock; |
33 | +import java.util.function.BiConsumer; | |
34 | 34 | import java.util.function.Consumer; |
35 | 35 | |
36 | 36 | @Service |
... | ... | @@ -43,7 +43,8 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { |
43 | 43 | |
44 | 44 | private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfilesMap = new ConcurrentHashMap<>(); |
45 | 45 | private final ConcurrentMap<DeviceId, DeviceProfileId> devicesMap = new ConcurrentHashMap<>(); |
46 | - private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, Consumer<DeviceProfile>>> listeners = new ConcurrentHashMap<>(); | |
46 | + private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, Consumer<DeviceProfile>>> profileListeners = new ConcurrentHashMap<>(); | |
47 | + private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, BiConsumer<DeviceId, DeviceProfile>>> deviceProfileListeners = new ConcurrentHashMap<>(); | |
47 | 48 | |
48 | 49 | public DefaultTbDeviceProfileCache(DeviceProfileService deviceProfileService, DeviceService deviceService) { |
49 | 50 | this.deviceProfileService = deviceProfileService; |
... | ... | @@ -88,32 +89,36 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { |
88 | 89 | } |
89 | 90 | |
90 | 91 | @Override |
91 | - public void put(DeviceProfile profile) { | |
92 | - if (profile.getId() != null) { | |
93 | - deviceProfilesMap.put(profile.getId(), profile); | |
94 | - log.debug("[{}] pushed device profile to cache: {}", profile.getId(), profile); | |
95 | - notifyListeners(profile); | |
96 | - } | |
97 | - } | |
98 | - | |
99 | - @Override | |
100 | 92 | public void evict(TenantId tenantId, DeviceProfileId profileId) { |
101 | 93 | DeviceProfile oldProfile = deviceProfilesMap.remove(profileId); |
102 | 94 | log.debug("[{}] evict device profile from cache: {}", profileId, oldProfile); |
103 | 95 | DeviceProfile newProfile = get(tenantId, profileId); |
104 | 96 | if (newProfile != null) { |
105 | - notifyListeners(newProfile); | |
97 | + notifyProfileListeners(newProfile); | |
106 | 98 | } |
107 | 99 | } |
108 | 100 | |
109 | 101 | @Override |
110 | - public void evict(DeviceId deviceId) { | |
111 | - devicesMap.remove(deviceId); | |
102 | + public void evict(TenantId tenantId, DeviceId deviceId) { | |
103 | + DeviceProfileId old = devicesMap.remove(deviceId); | |
104 | + if (old != null) { | |
105 | + DeviceProfile newProfile = get(tenantId, deviceId); | |
106 | + if (newProfile == null || !old.equals(newProfile.getId())) { | |
107 | + notifyDeviceListeners(tenantId, deviceId, newProfile); | |
108 | + } | |
109 | + } | |
112 | 110 | } |
113 | 111 | |
114 | 112 | @Override |
115 | - public void addListener(TenantId tenantId, EntityId listenerId, Consumer<DeviceProfile> listener) { | |
116 | - listeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, listener); | |
113 | + public void addListener(TenantId tenantId, EntityId listenerId, | |
114 | + Consumer<DeviceProfile> profileListener, | |
115 | + BiConsumer<DeviceId, DeviceProfile> deviceListener) { | |
116 | + if (profileListener != null) { | |
117 | + profileListeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, profileListener); | |
118 | + } | |
119 | + if (deviceListener != null) { | |
120 | + deviceProfileListeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, deviceListener); | |
121 | + } | |
117 | 122 | } |
118 | 123 | |
119 | 124 | @Override |
... | ... | @@ -128,17 +133,30 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { |
128 | 133 | |
129 | 134 | @Override |
130 | 135 | public void removeListener(TenantId tenantId, EntityId listenerId) { |
131 | - ConcurrentMap<EntityId, Consumer<DeviceProfile>> tenantListeners = listeners.get(tenantId); | |
136 | + ConcurrentMap<EntityId, Consumer<DeviceProfile>> tenantListeners = profileListeners.get(tenantId); | |
132 | 137 | if (tenantListeners != null) { |
133 | 138 | tenantListeners.remove(listenerId); |
134 | 139 | } |
140 | + ConcurrentMap<EntityId, BiConsumer<DeviceId, DeviceProfile>> deviceListeners = deviceProfileListeners.get(tenantId); | |
141 | + if (deviceListeners != null) { | |
142 | + deviceListeners.remove(listenerId); | |
143 | + } | |
135 | 144 | } |
136 | 145 | |
137 | - private void notifyListeners(DeviceProfile profile) { | |
138 | - ConcurrentMap<EntityId, Consumer<DeviceProfile>> tenantListeners = listeners.get(profile.getTenantId()); | |
146 | + private void notifyProfileListeners(DeviceProfile profile) { | |
147 | + ConcurrentMap<EntityId, Consumer<DeviceProfile>> tenantListeners = profileListeners.get(profile.getTenantId()); | |
139 | 148 | if (tenantListeners != null) { |
140 | 149 | tenantListeners.forEach((id, listener) -> listener.accept(profile)); |
141 | 150 | } |
142 | 151 | } |
143 | 152 | |
153 | + private void notifyDeviceListeners(TenantId tenantId, DeviceId deviceId, DeviceProfile profile) { | |
154 | + if (profile != null) { | |
155 | + ConcurrentMap<EntityId, BiConsumer<DeviceId, DeviceProfile>> tenantListeners = deviceProfileListeners.get(tenantId); | |
156 | + if (tenantListeners != null) { | |
157 | + tenantListeners.forEach((id, listener) -> listener.accept(deviceId, profile)); | |
158 | + } | |
159 | + } | |
160 | + } | |
161 | + | |
144 | 162 | } | ... | ... |
... | ... | @@ -23,11 +23,9 @@ import org.thingsboard.server.common.data.id.TenantId; |
23 | 23 | |
24 | 24 | public interface TbDeviceProfileCache extends RuleEngineDeviceProfileCache { |
25 | 25 | |
26 | - void put(DeviceProfile profile); | |
27 | - | |
28 | 26 | void evict(TenantId tenantId, DeviceProfileId id); |
29 | 27 | |
30 | - void evict(DeviceId id); | |
28 | + void evict(TenantId tenantId, DeviceId id); | |
31 | 29 | |
32 | 30 | DeviceProfile find(DeviceProfileId deviceProfileId); |
33 | 31 | ... | ... |
... | ... | @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Value; |
21 | 21 | import org.springframework.scheduling.annotation.Scheduled; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | 23 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
24 | +import org.thingsboard.server.common.data.ApiUsageState; | |
24 | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | 26 | import org.thingsboard.server.common.data.EntityType; |
26 | 27 | import org.thingsboard.server.common.data.HasName; |
... | ... | @@ -47,6 +48,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotifica |
47 | 48 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
48 | 49 | import org.thingsboard.server.queue.TbQueueCallback; |
49 | 50 | import org.thingsboard.server.queue.TbQueueProducer; |
51 | +import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper; | |
50 | 52 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
51 | 53 | import org.thingsboard.server.queue.discovery.PartitionService; |
52 | 54 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
... | ... | @@ -156,8 +158,16 @@ public class DefaultTbClusterService implements TbClusterService { |
156 | 158 | private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) { |
157 | 159 | if (deviceProfile != null) { |
158 | 160 | RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId(); |
159 | - if (targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId())) { | |
161 | + String targetQueueName = deviceProfile.getDefaultQueueName(); | |
162 | + boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); | |
163 | + boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); | |
164 | + | |
165 | + if (isRuleChainTransform && isQueueTransform) { | |
166 | + tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); | |
167 | + } else if (isRuleChainTransform) { | |
160 | 168 | tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId); |
169 | + } else if (isQueueTransform) { | |
170 | + tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName); | |
161 | 171 | } |
162 | 172 | } |
163 | 173 | return tbMsg; |
... | ... | @@ -207,6 +217,12 @@ public class DefaultTbClusterService implements TbClusterService { |
207 | 217 | } |
208 | 218 | |
209 | 219 | @Override |
220 | + public void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback) { | |
221 | + onEntityChange(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback); | |
222 | + broadcast(new ComponentLifecycleMsg(apiUsageState.getTenantId(), apiUsageState.getId(), ComponentLifecycleEvent.UPDATED)); | |
223 | + } | |
224 | + | |
225 | + @Override | |
210 | 226 | public void onDeviceProfileDelete(DeviceProfile entity, TbQueueCallback callback) { |
211 | 227 | onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback); |
212 | 228 | } |
... | ... | @@ -221,13 +237,14 @@ public class DefaultTbClusterService implements TbClusterService { |
221 | 237 | onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback); |
222 | 238 | } |
223 | 239 | |
224 | - public <T extends HasName> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { | |
225 | - log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entity.getName()); | |
240 | + public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { | |
241 | + String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName(); | |
242 | + log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName); | |
226 | 243 | TransportProtos.EntityUpdateMsg entityUpdateMsg = TransportProtos.EntityUpdateMsg.newBuilder() |
227 | 244 | .setEntityType(entityid.getEntityType().name()) |
228 | 245 | .setData(ByteString.copyFrom(encodingService.encode(entity))).build(); |
229 | 246 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityUpdateMsg(entityUpdateMsg).build(); |
230 | - broadcast(transportMsg); | |
247 | + broadcast(transportMsg, callback); | |
231 | 248 | } |
232 | 249 | |
233 | 250 | private void onEntityDelete(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) { |
... | ... | @@ -238,15 +255,16 @@ public class DefaultTbClusterService implements TbClusterService { |
238 | 255 | .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) |
239 | 256 | .build(); |
240 | 257 | ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setEntityDeleteMsg(entityDeleteMsg).build(); |
241 | - broadcast(transportMsg); | |
258 | + broadcast(transportMsg, callback); | |
242 | 259 | } |
243 | 260 | |
244 | - private void broadcast(ToTransportMsg transportMsg) { | |
261 | + private void broadcast(ToTransportMsg transportMsg, TbQueueCallback callback) { | |
245 | 262 | TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer(); |
246 | 263 | Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT); |
264 | + TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null; | |
247 | 265 | for (String transportServiceId : tbTransportServices) { |
248 | 266 | TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); |
249 | - toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null); | |
267 | + toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), proxyCallback); | |
250 | 268 | toTransportNfs.incrementAndGet(); |
251 | 269 | } |
252 | 270 | } |
... | ... | @@ -256,7 +274,8 @@ public class DefaultTbClusterService implements TbClusterService { |
256 | 274 | TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); |
257 | 275 | Set<String> tbRuleEngineServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE)); |
258 | 276 | if (msg.getEntityId().getEntityType().equals(EntityType.TENANT) |
259 | - || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE)) { | |
277 | + || msg.getEntityId().getEntityType().equals(EntityType.DEVICE_PROFILE) | |
278 | + || msg.getEntityId().getEntityType().equals(EntityType.API_USAGE_STATE)) { | |
260 | 279 | TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); |
261 | 280 | Set<String> tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); |
262 | 281 | for (String serviceId : tbCoreServices) { | ... | ... |
... | ... | @@ -17,41 +17,45 @@ package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Value; |
20 | +import org.springframework.boot.context.event.ApplicationReadyEvent; | |
21 | +import org.springframework.context.event.EventListener; | |
22 | +import org.springframework.core.annotation.Order; | |
20 | 23 | import org.springframework.scheduling.annotation.Scheduled; |
21 | 24 | import org.springframework.stereotype.Service; |
25 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
22 | 26 | import org.thingsboard.rule.engine.api.RpcError; |
23 | 27 | import org.thingsboard.server.actors.ActorSystemContext; |
24 | -import org.thingsboard.server.common.data.EntityType; | |
25 | 28 | import org.thingsboard.server.common.data.alarm.Alarm; |
26 | -import org.thingsboard.server.common.data.id.DeviceProfileId; | |
27 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 30 | import org.thingsboard.server.common.msg.MsgType; |
29 | 31 | import org.thingsboard.server.common.msg.TbActorMsg; |
30 | -import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; | |
31 | 32 | import org.thingsboard.server.common.msg.queue.ServiceType; |
32 | 33 | import org.thingsboard.server.common.msg.queue.TbCallback; |
34 | +import org.thingsboard.server.common.stats.StatsFactory; | |
35 | +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | |
33 | 36 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
34 | 37 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; |
35 | 38 | import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; |
36 | 39 | import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; |
37 | 40 | import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; |
38 | -import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeUpdateProto; | |
39 | -import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeDeleteProto; | |
40 | -import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; | |
41 | 41 | import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; |
42 | +import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; | |
43 | +import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeDeleteProto; | |
44 | +import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeUpdateProto; | |
42 | 45 | import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseProto; |
43 | 46 | import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; |
44 | 47 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
45 | 48 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; |
49 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
46 | 50 | import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
47 | 51 | import org.thingsboard.server.queue.TbQueueConsumer; |
48 | 52 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
49 | 53 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
50 | 54 | import org.thingsboard.server.queue.provider.TbCoreQueueFactory; |
51 | -import org.thingsboard.server.common.stats.StatsFactory; | |
52 | 55 | import org.thingsboard.server.queue.util.TbCoreComponent; |
53 | -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | |
56 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
54 | 57 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
58 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
55 | 59 | import org.thingsboard.server.service.queue.processing.AbstractConsumerService; |
56 | 60 | import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; |
57 | 61 | import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; |
... | ... | @@ -70,6 +74,8 @@ import java.util.UUID; |
70 | 74 | import java.util.concurrent.ConcurrentHashMap; |
71 | 75 | import java.util.concurrent.ConcurrentMap; |
72 | 76 | import java.util.concurrent.CountDownLatch; |
77 | +import java.util.concurrent.ExecutorService; | |
78 | +import java.util.concurrent.Executors; | |
73 | 79 | import java.util.concurrent.TimeUnit; |
74 | 80 | import java.util.function.Function; |
75 | 81 | import java.util.stream.Collectors; |
... | ... | @@ -88,32 +94,57 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
88 | 94 | |
89 | 95 | private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer; |
90 | 96 | private final DeviceStateService stateService; |
97 | + private final TbApiUsageStateService statsService; | |
91 | 98 | private final TbLocalSubscriptionService localSubscriptionService; |
92 | 99 | private final SubscriptionManagerService subscriptionManagerService; |
93 | 100 | private final TbCoreDeviceRpcService tbCoreDeviceRpcService; |
94 | 101 | private final TbCoreConsumerStats stats; |
102 | + protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer; | |
103 | + | |
104 | + protected volatile ExecutorService usageStatsExecutor; | |
95 | 105 | |
96 | - public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext, | |
97 | - DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService, | |
98 | - SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService, | |
99 | - TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) { | |
100 | - super(actorContext, encodingService, deviceProfileCache, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); | |
106 | + public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, | |
107 | + ActorSystemContext actorContext, | |
108 | + DeviceStateService stateService, | |
109 | + TbLocalSubscriptionService localSubscriptionService, | |
110 | + SubscriptionManagerService subscriptionManagerService, | |
111 | + DataDecodingEncodingService encodingService, | |
112 | + TbCoreDeviceRpcService tbCoreDeviceRpcService, | |
113 | + StatsFactory statsFactory, | |
114 | + TbDeviceProfileCache deviceProfileCache, | |
115 | + TbApiUsageStateService statsService, | |
116 | + TbTenantProfileCache tenantProfileCache, | |
117 | + TbApiUsageStateService apiUsageStateService) { | |
118 | + super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); | |
101 | 119 | this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); |
120 | + this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer(); | |
102 | 121 | this.stateService = stateService; |
103 | 122 | this.localSubscriptionService = localSubscriptionService; |
104 | 123 | this.subscriptionManagerService = subscriptionManagerService; |
105 | 124 | this.tbCoreDeviceRpcService = tbCoreDeviceRpcService; |
106 | 125 | this.stats = new TbCoreConsumerStats(statsFactory); |
126 | + this.statsService = statsService; | |
107 | 127 | } |
108 | 128 | |
109 | 129 | @PostConstruct |
110 | 130 | public void init() { |
111 | 131 | super.init("tb-core-consumer", "tb-core-notifications-consumer"); |
132 | + this.usageStatsExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("tb-core-usage-stats-consumer")); | |
112 | 133 | } |
113 | 134 | |
114 | 135 | @PreDestroy |
115 | 136 | public void destroy() { |
116 | 137 | super.destroy(); |
138 | + if (usageStatsExecutor != null) { | |
139 | + usageStatsExecutor.shutdownNow(); | |
140 | + } | |
141 | + } | |
142 | + | |
143 | + @EventListener(ApplicationReadyEvent.class) | |
144 | + @Order(value = 2) | |
145 | + public void onApplicationEvent(ApplicationReadyEvent event) { | |
146 | + super.onApplicationEvent(event); | |
147 | + launchUsageStatsConsumer(); | |
117 | 148 | } |
118 | 149 | |
119 | 150 | @Override |
... | ... | @@ -121,6 +152,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
121 | 152 | if (partitionChangeEvent.getServiceType().equals(getServiceType())) { |
122 | 153 | log.info("Subscribing to partitions: {}", partitionChangeEvent.getPartitions()); |
123 | 154 | this.mainConsumer.subscribe(partitionChangeEvent.getPartitions()); |
155 | + this.usageStatsConsumer.subscribe( | |
156 | + partitionChangeEvent | |
157 | + .getPartitions() | |
158 | + .stream() | |
159 | + .map(tpi -> tpi.newByTopic(usageStatsConsumer.getTopic())) | |
160 | + .collect(Collectors.toSet())); | |
124 | 161 | } |
125 | 162 | } |
126 | 163 | |
... | ... | @@ -223,6 +260,53 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
223 | 260 | } |
224 | 261 | } |
225 | 262 | |
263 | + private void launchUsageStatsConsumer() { | |
264 | + usageStatsExecutor.submit(() -> { | |
265 | + while (!stopped) { | |
266 | + try { | |
267 | + List<TbProtoQueueMsg<ToUsageStatsServiceMsg>> msgs = usageStatsConsumer.poll(getNotificationPollDuration()); | |
268 | + if (msgs.isEmpty()) { | |
269 | + continue; | |
270 | + } | |
271 | + ConcurrentMap<UUID, TbProtoQueueMsg<ToUsageStatsServiceMsg>> pendingMap = msgs.stream().collect( | |
272 | + Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity())); | |
273 | + CountDownLatch processingTimeoutLatch = new CountDownLatch(1); | |
274 | + TbPackProcessingContext<TbProtoQueueMsg<ToUsageStatsServiceMsg>> ctx = new TbPackProcessingContext<>( | |
275 | + processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>()); | |
276 | + pendingMap.forEach((id, msg) -> { | |
277 | + log.trace("[{}] Creating usage stats callback for message: {}", id, msg.getValue()); | |
278 | + TbCallback callback = new TbPackCallback<>(id, ctx); | |
279 | + try { | |
280 | + handleUsageStats(msg, callback); | |
281 | + } catch (Throwable e) { | |
282 | + log.warn("[{}] Failed to process usge stats: {}", id, msg, e); | |
283 | + callback.onFailure(e); | |
284 | + } | |
285 | + }); | |
286 | + if (!processingTimeoutLatch.await(getNotificationPackProcessingTimeout(), TimeUnit.MILLISECONDS)) { | |
287 | + ctx.getAckMap().forEach((id, msg) -> log.warn("[{}] Timeout to process usage stats: {}", id, msg.getValue())); | |
288 | + ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process usage stats: {}", id, msg.getValue())); | |
289 | + } | |
290 | + usageStatsConsumer.commit(); | |
291 | + } catch (Exception e) { | |
292 | + if (!stopped) { | |
293 | + log.warn("Failed to obtain usage stats from queue.", e); | |
294 | + try { | |
295 | + Thread.sleep(getNotificationPollDuration()); | |
296 | + } catch (InterruptedException e2) { | |
297 | + log.trace("Failed to wait until the server has capacity to handle new usage stats", e2); | |
298 | + } | |
299 | + } | |
300 | + } | |
301 | + } | |
302 | + log.info("TB Usage Stats Consumer stopped."); | |
303 | + }); | |
304 | + } | |
305 | + | |
306 | + private void handleUsageStats(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { | |
307 | + statsService.process(msg, callback); | |
308 | + } | |
309 | + | |
226 | 310 | private void forwardToCoreRpcService(FromDeviceRPCResponseProto proto, TbCallback callback) { |
227 | 311 | RpcError error = proto.getError() > 0 ? RpcError.values()[proto.getError()] : null; |
228 | 312 | FromDeviceRpcResponse response = new FromDeviceRpcResponse(new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()) |
... | ... | @@ -321,6 +405,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
321 | 405 | if (mainConsumer != null) { |
322 | 406 | mainConsumer.unsubscribe(); |
323 | 407 | } |
408 | + if (usageStatsConsumer != null) { | |
409 | + usageStatsConsumer.unsubscribe(); | |
410 | + } | |
324 | 411 | } |
325 | 412 | |
326 | 413 | } | ... | ... |
... | ... | @@ -15,7 +15,6 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | -import com.google.protobuf.ByteString; | |
19 | 18 | import com.google.protobuf.ProtocolStringList; |
20 | 19 | import lombok.extern.slf4j.Slf4j; |
21 | 20 | import org.springframework.beans.factory.annotation.Value; |
... | ... | @@ -24,10 +23,16 @@ import org.springframework.stereotype.Service; |
24 | 23 | import org.thingsboard.rule.engine.api.RpcError; |
25 | 24 | import org.thingsboard.server.actors.ActorSystemContext; |
26 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
27 | -import org.thingsboard.server.common.msg.TbActorMsg; | |
28 | 26 | import org.thingsboard.server.common.msg.TbMsg; |
29 | -import org.thingsboard.server.common.msg.queue.*; | |
27 | +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; | |
28 | +import org.thingsboard.server.common.msg.queue.RuleEngineException; | |
29 | +import org.thingsboard.server.common.msg.queue.RuleNodeInfo; | |
30 | +import org.thingsboard.server.common.msg.queue.ServiceQueue; | |
31 | +import org.thingsboard.server.common.msg.queue.ServiceType; | |
32 | +import org.thingsboard.server.common.msg.queue.TbCallback; | |
33 | +import org.thingsboard.server.common.msg.queue.TbMsgCallback; | |
30 | 34 | import org.thingsboard.server.common.stats.StatsFactory; |
35 | +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | |
31 | 36 | import org.thingsboard.server.gen.transport.TransportProtos; |
32 | 37 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
33 | 38 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
... | ... | @@ -38,17 +43,33 @@ import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; |
38 | 43 | import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; |
39 | 44 | import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; |
40 | 45 | import org.thingsboard.server.queue.util.TbRuleEngineComponent; |
41 | -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; | |
46 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
42 | 47 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
43 | -import org.thingsboard.server.service.queue.processing.*; | |
48 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
49 | +import org.thingsboard.server.service.queue.processing.AbstractConsumerService; | |
50 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision; | |
51 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; | |
52 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategy; | |
53 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; | |
54 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; | |
55 | +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; | |
44 | 56 | import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; |
45 | 57 | import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; |
46 | 58 | import org.thingsboard.server.service.stats.RuleEngineStatisticsService; |
47 | 59 | |
48 | 60 | import javax.annotation.PostConstruct; |
49 | 61 | import javax.annotation.PreDestroy; |
50 | -import java.util.*; | |
51 | -import java.util.concurrent.*; | |
62 | +import java.util.Collections; | |
63 | +import java.util.HashSet; | |
64 | +import java.util.List; | |
65 | +import java.util.Map; | |
66 | +import java.util.Set; | |
67 | +import java.util.UUID; | |
68 | +import java.util.concurrent.ConcurrentHashMap; | |
69 | +import java.util.concurrent.ConcurrentMap; | |
70 | +import java.util.concurrent.ExecutorService; | |
71 | +import java.util.concurrent.Executors; | |
72 | +import java.util.concurrent.TimeUnit; | |
52 | 73 | |
53 | 74 | @Service |
54 | 75 | @TbRuleEngineComponent |
... | ... | @@ -79,11 +100,16 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< |
79 | 100 | public DefaultTbRuleEngineConsumerService(TbRuleEngineProcessingStrategyFactory processingStrategyFactory, |
80 | 101 | TbRuleEngineSubmitStrategyFactory submitStrategyFactory, |
81 | 102 | TbQueueRuleEngineSettings ruleEngineSettings, |
82 | - TbRuleEngineQueueFactory tbRuleEngineQueueFactory, RuleEngineStatisticsService statisticsService, | |
83 | - ActorSystemContext actorContext, DataDecodingEncodingService encodingService, | |
103 | + TbRuleEngineQueueFactory tbRuleEngineQueueFactory, | |
104 | + RuleEngineStatisticsService statisticsService, | |
105 | + ActorSystemContext actorContext, | |
106 | + DataDecodingEncodingService encodingService, | |
84 | 107 | TbRuleEngineDeviceRpcService tbDeviceRpcService, |
85 | - StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) { | |
86 | - super(actorContext, encodingService, deviceProfileCache, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer()); | |
108 | + StatsFactory statsFactory, | |
109 | + TbDeviceProfileCache deviceProfileCache, | |
110 | + TbTenantProfileCache tenantProfileCache, | |
111 | + TbApiUsageStateService apiUsageStateService) { | |
112 | + super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer()); | |
87 | 113 | this.statisticsService = statisticsService; |
88 | 114 | this.ruleEngineSettings = ruleEngineSettings; |
89 | 115 | this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory; | ... | ... |
... | ... | @@ -21,11 +21,10 @@ import org.springframework.stereotype.Service; |
21 | 21 | import org.thingsboard.server.common.data.Tenant; |
22 | 22 | import org.thingsboard.server.common.data.TenantProfile; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | -import org.thingsboard.server.dao.tenant.TenantProfileService; | |
25 | 24 | import org.thingsboard.server.dao.tenant.TenantService; |
26 | 25 | import org.thingsboard.server.queue.discovery.TenantRoutingInfo; |
27 | 26 | import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; |
28 | -import org.thingsboard.server.service.profile.TbTenantProfileCache; | |
27 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
29 | 28 | |
30 | 29 | @Slf4j |
31 | 30 | @Service | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
19 | +import org.thingsboard.server.common.data.ApiUsageState; | |
19 | 20 | import org.thingsboard.server.common.data.DeviceProfile; |
20 | 21 | import org.thingsboard.server.common.data.Tenant; |
21 | 22 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -63,4 +64,6 @@ public interface TbClusterService { |
63 | 64 | void onTenantChange(Tenant tenant, TbQueueCallback callback); |
64 | 65 | |
65 | 66 | void onTenantDelete(Tenant tenant, TbQueueCallback callback); |
67 | + | |
68 | + void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback); | |
66 | 69 | } | ... | ... |
... | ... | @@ -20,11 +20,14 @@ import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
21 | 21 | import org.springframework.context.ApplicationListener; |
22 | 22 | import org.springframework.context.event.EventListener; |
23 | +import org.springframework.core.annotation.Order; | |
23 | 24 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
24 | 25 | import org.thingsboard.server.actors.ActorSystemContext; |
25 | 26 | import org.thingsboard.server.common.data.EntityType; |
26 | 27 | import org.thingsboard.server.common.data.id.DeviceId; |
27 | 28 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
29 | +import org.thingsboard.server.common.data.id.TenantProfileId; | |
30 | +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | |
28 | 31 | import org.thingsboard.server.common.msg.TbActorMsg; |
29 | 32 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
30 | 33 | import org.thingsboard.server.common.msg.queue.ServiceType; |
... | ... | @@ -33,7 +36,9 @@ import org.thingsboard.server.queue.TbQueueConsumer; |
33 | 36 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
34 | 37 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
35 | 38 | import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
39 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
36 | 40 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
41 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
37 | 42 | import org.thingsboard.server.service.queue.TbPackCallback; |
38 | 43 | import org.thingsboard.server.service.queue.TbPackProcessingContext; |
39 | 44 | |
... | ... | @@ -59,15 +64,19 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene |
59 | 64 | |
60 | 65 | protected final ActorSystemContext actorContext; |
61 | 66 | protected final DataDecodingEncodingService encodingService; |
67 | + protected final TbTenantProfileCache tenantProfileCache; | |
62 | 68 | protected final TbDeviceProfileCache deviceProfileCache; |
69 | + protected final TbApiUsageStateService apiUsageStateService; | |
63 | 70 | |
64 | 71 | protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer; |
65 | 72 | |
66 | 73 | public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, |
67 | - TbDeviceProfileCache deviceProfileCache, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { | |
74 | + TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache, TbApiUsageStateService apiUsageStateService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { | |
68 | 75 | this.actorContext = actorContext; |
69 | 76 | this.encodingService = encodingService; |
77 | + this.tenantProfileCache = tenantProfileCache; | |
70 | 78 | this.deviceProfileCache = deviceProfileCache; |
79 | + this.apiUsageStateService = apiUsageStateService; | |
71 | 80 | this.nfConsumer = nfConsumer; |
72 | 81 | } |
73 | 82 | |
... | ... | @@ -77,6 +86,7 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene |
77 | 86 | } |
78 | 87 | |
79 | 88 | @EventListener(ApplicationReadyEvent.class) |
89 | + @Order(value = 2) | |
80 | 90 | public void onApplicationEvent(ApplicationReadyEvent event) { |
81 | 91 | log.info("Subscribing to notifications: {}", nfConsumer.getTopic()); |
82 | 92 | this.nfConsumer.subscribe(); |
... | ... | @@ -143,10 +153,23 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene |
143 | 153 | TbActorMsg actorMsg = actorMsgOpt.get(); |
144 | 154 | if (actorMsg instanceof ComponentLifecycleMsg) { |
145 | 155 | ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; |
146 | - if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | |
156 | + if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | |
157 | + TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); | |
158 | + tenantProfileCache.evict(tenantProfileId); | |
159 | + if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { | |
160 | + apiUsageStateService.onTenantProfileUpdate(tenantProfileId); | |
161 | + } | |
162 | + } else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | |
163 | + tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); | |
164 | + if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { | |
165 | + apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId()); | |
166 | + } | |
167 | + } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | |
147 | 168 | deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); |
148 | 169 | } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { |
149 | - deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId())); | |
170 | + deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId())); | |
171 | + } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { | |
172 | + apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); | |
150 | 173 | } |
151 | 174 | } |
152 | 175 | log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); | ... | ... |
... | ... | @@ -19,6 +19,10 @@ import com.google.common.util.concurrent.Futures; |
19 | 19 | import com.google.common.util.concurrent.ListenableFuture; |
20 | 20 | import lombok.extern.slf4j.Slf4j; |
21 | 21 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
22 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
23 | +import org.thingsboard.server.common.data.id.TenantId; | |
24 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
25 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
22 | 26 | |
23 | 27 | import java.util.Map; |
24 | 28 | import java.util.UUID; |
... | ... | @@ -33,9 +37,16 @@ import java.util.concurrent.atomic.AtomicInteger; |
33 | 37 | @Slf4j |
34 | 38 | public abstract class AbstractJsInvokeService implements JsInvokeService { |
35 | 39 | |
40 | + private final TbApiUsageStateService apiUsageStateService; | |
41 | + private final TbApiUsageClient apiUsageClient; | |
36 | 42 | protected ScheduledExecutorService timeoutExecutorService; |
37 | 43 | protected Map<UUID, String> scriptIdToNameMap = new ConcurrentHashMap<>(); |
38 | - protected Map<UUID, BlackListInfo> blackListedFunctions = new ConcurrentHashMap<>(); | |
44 | + protected Map<UUID, DisableListInfo> disabledFunctions = new ConcurrentHashMap<>(); | |
45 | + | |
46 | + protected AbstractJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) { | |
47 | + this.apiUsageStateService = apiUsageStateService; | |
48 | + this.apiUsageClient = apiUsageClient; | |
49 | + } | |
39 | 50 | |
40 | 51 | public void init(long maxRequestsTimeout) { |
41 | 52 | if (maxRequestsTimeout > 0) { |
... | ... | @@ -50,24 +61,33 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { |
50 | 61 | } |
51 | 62 | |
52 | 63 | @Override |
53 | - public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) { | |
54 | - UUID scriptId = UUID.randomUUID(); | |
55 | - String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_'); | |
56 | - String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); | |
57 | - return doEval(scriptId, functionName, jsScript); | |
64 | + public ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) { | |
65 | + if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { | |
66 | + UUID scriptId = UUID.randomUUID(); | |
67 | + String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_'); | |
68 | + String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); | |
69 | + return doEval(scriptId, functionName, jsScript); | |
70 | + } else { | |
71 | + return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!")); | |
72 | + } | |
58 | 73 | } |
59 | 74 | |
60 | 75 | @Override |
61 | - public ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args) { | |
62 | - String functionName = scriptIdToNameMap.get(scriptId); | |
63 | - if (functionName == null) { | |
64 | - return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!")); | |
65 | - } | |
66 | - if (!isBlackListed(scriptId)) { | |
67 | - return doInvokeFunction(scriptId, functionName, args); | |
76 | + public ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args) { | |
77 | + if (apiUsageStateService.getApiUsageState(tenantId).isJsExecEnabled()) { | |
78 | + String functionName = scriptIdToNameMap.get(scriptId); | |
79 | + if (functionName == null) { | |
80 | + return Futures.immediateFailedFuture(new RuntimeException("No compiled script found for scriptId: [" + scriptId + "]!")); | |
81 | + } | |
82 | + if (!isDisabled(scriptId)) { | |
83 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.JS_EXEC_COUNT, 1); | |
84 | + return doInvokeFunction(scriptId, functionName, args); | |
85 | + } else { | |
86 | + return Futures.immediateFailedFuture( | |
87 | + new RuntimeException("Script invocation is blocked due to maximum error count " + getMaxErrors() + "!")); | |
88 | + } | |
68 | 89 | } else { |
69 | - return Futures.immediateFailedFuture( | |
70 | - new RuntimeException("Script is blacklisted due to maximum error count " + getMaxErrors() + "!")); | |
90 | + return Futures.immediateFailedFuture(new RuntimeException("JS Execution is disabled due to API limits!")); | |
71 | 91 | } |
72 | 92 | } |
73 | 93 | |
... | ... | @@ -77,7 +97,7 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { |
77 | 97 | if (functionName != null) { |
78 | 98 | try { |
79 | 99 | scriptIdToNameMap.remove(scriptId); |
80 | - blackListedFunctions.remove(scriptId); | |
100 | + disabledFunctions.remove(scriptId); | |
81 | 101 | doRelease(scriptId, functionName); |
82 | 102 | } catch (Exception e) { |
83 | 103 | return Futures.immediateFailedFuture(e); |
... | ... | @@ -97,7 +117,7 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { |
97 | 117 | protected abstract long getMaxBlacklistDuration(); |
98 | 118 | |
99 | 119 | protected void onScriptExecutionError(UUID scriptId) { |
100 | - blackListedFunctions.computeIfAbsent(scriptId, key -> new BlackListInfo()).incrementAndGet(); | |
120 | + disabledFunctions.computeIfAbsent(scriptId, key -> new DisableListInfo()).incrementAndGet(); | |
101 | 121 | } |
102 | 122 | |
103 | 123 | private String generateJsScript(JsScriptType scriptType, String functionName, String scriptBody, String... argNames) { |
... | ... | @@ -107,11 +127,11 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { |
107 | 127 | throw new RuntimeException("No script factory implemented for scriptType: " + scriptType); |
108 | 128 | } |
109 | 129 | |
110 | - private boolean isBlackListed(UUID scriptId) { | |
111 | - BlackListInfo errorCount = blackListedFunctions.get(scriptId); | |
130 | + private boolean isDisabled(UUID scriptId) { | |
131 | + DisableListInfo errorCount = disabledFunctions.get(scriptId); | |
112 | 132 | if (errorCount != null) { |
113 | 133 | if (errorCount.getExpirationTime() <= System.currentTimeMillis()) { |
114 | - blackListedFunctions.remove(scriptId); | |
134 | + disabledFunctions.remove(scriptId); | |
115 | 135 | return false; |
116 | 136 | } else { |
117 | 137 | return errorCount.get() >= getMaxErrors(); |
... | ... | @@ -121,11 +141,11 @@ public abstract class AbstractJsInvokeService implements JsInvokeService { |
121 | 141 | } |
122 | 142 | } |
123 | 143 | |
124 | - private class BlackListInfo { | |
144 | + private class DisableListInfo { | |
125 | 145 | private final AtomicInteger counter; |
126 | 146 | private long expirationTime; |
127 | 147 | |
128 | - private BlackListInfo() { | |
148 | + private DisableListInfo() { | |
129 | 149 | this.counter = new AtomicInteger(0); |
130 | 150 | } |
131 | 151 | ... | ... |
... | ... | @@ -24,10 +24,10 @@ import delight.nashornsandbox.NashornSandboxes; |
24 | 24 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; |
25 | 25 | import lombok.Getter; |
26 | 26 | import lombok.extern.slf4j.Slf4j; |
27 | -import org.springframework.beans.factory.annotation.Autowired; | |
28 | 27 | import org.springframework.beans.factory.annotation.Value; |
29 | 28 | import org.springframework.scheduling.annotation.Scheduled; |
30 | -import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
29 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
30 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
31 | 31 | |
32 | 32 | import javax.annotation.PostConstruct; |
33 | 33 | import javax.annotation.PreDestroy; |
... | ... | @@ -38,7 +38,6 @@ import java.util.UUID; |
38 | 38 | import java.util.concurrent.ExecutionException; |
39 | 39 | import java.util.concurrent.ExecutorService; |
40 | 40 | import java.util.concurrent.Executors; |
41 | -import java.util.concurrent.ScheduledExecutorService; | |
42 | 41 | import java.util.concurrent.TimeUnit; |
43 | 42 | import java.util.concurrent.atomic.AtomicInteger; |
44 | 43 | |
... | ... | @@ -57,9 +56,8 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer |
57 | 56 | private final FutureCallback<UUID> evalCallback = new JsStatCallback<>(jsEvalMsgs, jsTimeoutMsgs, jsFailedMsgs); |
58 | 57 | private final FutureCallback<Object> invokeCallback = new JsStatCallback<>(jsInvokeMsgs, jsTimeoutMsgs, jsFailedMsgs); |
59 | 58 | |
60 | - @Autowired | |
61 | 59 | @Getter |
62 | - private JsExecutorService jsExecutor; | |
60 | + private final JsExecutorService jsExecutor; | |
63 | 61 | |
64 | 62 | @Value("${js.local.max_requests_timeout:0}") |
65 | 63 | private long maxRequestsTimeout; |
... | ... | @@ -67,6 +65,11 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer |
67 | 65 | @Value("${js.local.stats.enabled:false}") |
68 | 66 | private boolean statsEnabled; |
69 | 67 | |
68 | + public AbstractNashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) { | |
69 | + super(apiUsageStateService, apiUsageClient); | |
70 | + this.jsExecutor = jsExecutor; | |
71 | + } | |
72 | + | |
70 | 73 | @Scheduled(fixedDelayString = "${js.local.stats.print_interval_ms:10000}") |
71 | 74 | public void printStats() { |
72 | 75 | if (statsEnabled) { | ... | ... |
... | ... | @@ -17,14 +17,15 @@ package org.thingsboard.server.service.script; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.id.EntityId; |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
20 | 21 | |
21 | 22 | import java.util.UUID; |
22 | 23 | |
23 | 24 | public interface JsInvokeService { |
24 | 25 | |
25 | - ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames); | |
26 | + ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames); | |
26 | 27 | |
27 | - ListenableFuture<Object> invokeFunction(UUID scriptId, Object... args); | |
28 | + ListenableFuture<Object> invokeFunction(TenantId tenantId, UUID scriptId, Object... args); | |
28 | 29 | |
29 | 30 | ListenableFuture<Void> release(UUID scriptId); |
30 | 31 | ... | ... |
... | ... | @@ -19,6 +19,8 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Value; |
20 | 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
21 | 21 | import org.springframework.stereotype.Service; |
22 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
23 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
22 | 24 | |
23 | 25 | import java.util.concurrent.TimeUnit; |
24 | 26 | |
... | ... | @@ -42,6 +44,10 @@ public class NashornJsInvokeService extends AbstractNashornJsInvokeService { |
42 | 44 | @Value("${js.local.max_black_list_duration_sec:60}") |
43 | 45 | private int maxBlackListDurationSec; |
44 | 46 | |
47 | + public NashornJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient, JsExecutorService jsExecutor) { | |
48 | + super(apiUsageStateService, apiUsageClient, jsExecutor); | |
49 | + } | |
50 | + | |
45 | 51 | @Override |
46 | 52 | protected boolean useJsSandbox() { |
47 | 53 | return useJsSandbox; | ... | ... |
... | ... | @@ -30,6 +30,8 @@ import org.thingsboard.server.gen.js.JsInvokeProtos; |
30 | 30 | import org.thingsboard.server.queue.TbQueueRequestTemplate; |
31 | 31 | import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; |
32 | 32 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
33 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
34 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
33 | 35 | |
34 | 36 | import javax.annotation.Nullable; |
35 | 37 | import javax.annotation.PostConstruct; |
... | ... | @@ -68,6 +70,10 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { |
68 | 70 | private final AtomicInteger queueFailedMsgs = new AtomicInteger(0); |
69 | 71 | private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0); |
70 | 72 | |
73 | + public RemoteJsInvokeService(TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) { | |
74 | + super(apiUsageStateService, apiUsageClient); | |
75 | + } | |
76 | + | |
71 | 77 | @Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}") |
72 | 78 | public void printStats() { |
73 | 79 | if (statsEnabled) { | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.MoreExecutors; |
25 | 25 | import lombok.extern.slf4j.Slf4j; |
26 | 26 | import org.apache.commons.lang3.StringUtils; |
27 | 27 | import org.thingsboard.server.common.data.id.EntityId; |
28 | +import org.thingsboard.server.common.data.id.TenantId; | |
28 | 29 | import org.thingsboard.server.common.msg.TbMsg; |
29 | 30 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
30 | 31 | |
... | ... | @@ -43,13 +44,15 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S |
43 | 44 | private final JsInvokeService sandboxService; |
44 | 45 | |
45 | 46 | private final UUID scriptId; |
47 | + private final TenantId tenantId; | |
46 | 48 | private final EntityId entityId; |
47 | 49 | |
48 | - public RuleNodeJsScriptEngine(JsInvokeService sandboxService, EntityId entityId, String script, String... argNames) { | |
50 | + public RuleNodeJsScriptEngine(TenantId tenantId, JsInvokeService sandboxService, EntityId entityId, String script, String... argNames) { | |
51 | + this.tenantId = tenantId; | |
49 | 52 | this.sandboxService = sandboxService; |
50 | 53 | this.entityId = entityId; |
51 | 54 | try { |
52 | - this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); | |
55 | + this.scriptId = this.sandboxService.eval(tenantId, JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); | |
53 | 56 | } catch (Exception e) { |
54 | 57 | Throwable t = e; |
55 | 58 | if (e instanceof ExecutionException) { |
... | ... | @@ -203,7 +206,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S |
203 | 206 | private JsonNode executeScript(TbMsg msg) throws ScriptException { |
204 | 207 | try { |
205 | 208 | String[] inArgs = prepareArgs(msg); |
206 | - String eval = sandboxService.invokeFunction(this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString(); | |
209 | + String eval = sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]).get().toString(); | |
207 | 210 | return mapper.readTree(eval); |
208 | 211 | } catch (ExecutionException e) { |
209 | 212 | if (e.getCause() instanceof ScriptException) { |
... | ... | @@ -220,7 +223,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S |
220 | 223 | |
221 | 224 | private ListenableFuture<JsonNode> executeScriptAsync(TbMsg msg) { |
222 | 225 | String[] inArgs = prepareArgs(msg); |
223 | - return Futures.transformAsync(sandboxService.invokeFunction(this.scriptId, inArgs[0], inArgs[1], inArgs[2]), | |
226 | + return Futures.transformAsync(sandboxService.invokeFunction(tenantId, this.scriptId, inArgs[0], inArgs[1], inArgs[2]), | |
224 | 227 | o -> { |
225 | 228 | try { |
226 | 229 | return Futures.immediateFuture(mapper.readTree(o.toString())); | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import org.springframework.http.ResponseEntity; |
25 | 25 | import org.springframework.stereotype.Component; |
26 | 26 | import org.springframework.web.context.request.async.DeferredResult; |
27 | 27 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
28 | +import org.thingsboard.server.common.data.ApiUsageState; | |
28 | 29 | import org.thingsboard.server.common.data.Customer; |
29 | 30 | import org.thingsboard.server.common.data.Device; |
30 | 31 | import org.thingsboard.server.common.data.DeviceProfile; |
... | ... | @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.Tenant; |
33 | 34 | import org.thingsboard.server.common.data.User; |
34 | 35 | import org.thingsboard.server.common.data.asset.Asset; |
35 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | +import org.thingsboard.server.common.data.id.ApiUsageStateId; | |
36 | 38 | import org.thingsboard.server.common.data.id.AssetId; |
37 | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | 40 | import org.thingsboard.server.common.data.id.DeviceId; |
... | ... | @@ -53,8 +55,10 @@ import org.thingsboard.server.dao.customer.CustomerService; |
53 | 55 | import org.thingsboard.server.dao.device.DeviceProfileService; |
54 | 56 | import org.thingsboard.server.dao.device.DeviceService; |
55 | 57 | import org.thingsboard.server.dao.entityview.EntityViewService; |
58 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | |
56 | 59 | import org.thingsboard.server.dao.rule.RuleChainService; |
57 | 60 | import org.thingsboard.server.dao.tenant.TenantService; |
61 | +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | |
58 | 62 | import org.thingsboard.server.dao.user.UserService; |
59 | 63 | import org.thingsboard.server.service.security.model.SecurityUser; |
60 | 64 | import org.thingsboard.server.service.security.permission.AccessControlService; |
... | ... | @@ -111,6 +115,9 @@ public class AccessValidator { |
111 | 115 | @Autowired |
112 | 116 | protected AccessControlService accessControlService; |
113 | 117 | |
118 | + @Autowired | |
119 | + protected ApiUsageStateService apiUsageStateService; | |
120 | + | |
114 | 121 | private ExecutorService executor; |
115 | 122 | |
116 | 123 | @PostConstruct |
... | ... | @@ -152,7 +159,11 @@ public class AccessValidator { |
152 | 159 | new FutureCallback<DeferredResult<ResponseEntity>>() { |
153 | 160 | @Override |
154 | 161 | public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { |
155 | - onSuccess.accept(response, currentUser.getTenantId(), entityId); | |
162 | + try { | |
163 | + onSuccess.accept(response, currentUser.getTenantId(), entityId); | |
164 | + } catch (Exception e) { | |
165 | + onFailure(e); | |
166 | + } | |
156 | 167 | } |
157 | 168 | |
158 | 169 | @Override |
... | ... | @@ -193,6 +204,9 @@ public class AccessValidator { |
193 | 204 | case ENTITY_VIEW: |
194 | 205 | validateEntityView(currentUser, operation, entityId, callback); |
195 | 206 | return; |
207 | + case API_USAGE_STATE: | |
208 | + validateApiUsageState(currentUser, operation, entityId, callback); | |
209 | + return; | |
196 | 210 | default: |
197 | 211 | //TODO: add support of other entities |
198 | 212 | throw new IllegalStateException("Not Implemented!"); |
... | ... | @@ -237,6 +251,27 @@ public class AccessValidator { |
237 | 251 | } |
238 | 252 | } |
239 | 253 | |
254 | + private void validateApiUsageState(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
255 | + if (currentUser.isSystemAdmin()) { | |
256 | + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
257 | + } else { | |
258 | + if (!operation.equals(Operation.READ_TELEMETRY)) { | |
259 | + callback.onSuccess(ValidationResult.accessDenied("Allowed only READ_TELEMETRY operation!")); | |
260 | + } | |
261 | + ApiUsageState apiUsageState = apiUsageStateService.findApiUsageStateById(currentUser.getTenantId(), new ApiUsageStateId(entityId.getId())); | |
262 | + if (apiUsageState == null) { | |
263 | + callback.onSuccess(ValidationResult.entityNotFound("Api Usage State with requested id wasn't found!")); | |
264 | + } else { | |
265 | + try { | |
266 | + accessControlService.checkPermission(currentUser, Resource.API_USAGE_STATE, operation, entityId, apiUsageState); | |
267 | + } catch (ThingsboardException e) { | |
268 | + callback.onSuccess(ValidationResult.accessDenied(e.getMessage())); | |
269 | + } | |
270 | + callback.onSuccess(ValidationResult.ok(apiUsageState)); | |
271 | + } | |
272 | + } | |
273 | + } | |
274 | + | |
240 | 275 | private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { |
241 | 276 | if (currentUser.isSystemAdmin()) { |
242 | 277 | callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); |
... | ... | @@ -404,9 +439,9 @@ public class AccessValidator { |
404 | 439 | |
405 | 440 | public static void handleError(Throwable e, final DeferredResult<ResponseEntity> response, HttpStatus defaultErrorStatus) { |
406 | 441 | ResponseEntity responseEntity; |
407 | - if (e != null && e instanceof ToErrorResponseEntity) { | |
442 | + if (e instanceof ToErrorResponseEntity) { | |
408 | 443 | responseEntity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); |
409 | - } else if (e != null && e instanceof IllegalArgumentException) { | |
444 | + } else if (e instanceof IllegalArgumentException || e instanceof IncorrectParameterException) { | |
410 | 445 | responseEntity = new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); |
411 | 446 | } else { |
412 | 447 | responseEntity = new ResponseEntity<>(defaultErrorStatus); | ... | ... |
... | ... | @@ -30,7 +30,7 @@ import java.nio.charset.StandardCharsets; |
30 | 30 | |
31 | 31 | @Component(value = "oauth2AuthenticationFailureHandler") |
32 | 32 | @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") |
33 | -public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | |
33 | +public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { | |
34 | 34 | |
35 | 35 | @Override |
36 | 36 | public void onAuthenticationFailure(HttpServletRequest request, | ... | ... |
... | ... | @@ -63,7 +63,6 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS |
63 | 63 | public void onAuthenticationSuccess(HttpServletRequest request, |
64 | 64 | HttpServletResponse response, |
65 | 65 | Authentication authentication) throws IOException { |
66 | - | |
67 | 66 | String baseUrl = MiscUtils.constructBaseUrl(request); |
68 | 67 | try { |
69 | 68 | OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; | ... | ... |
... | ... | @@ -35,7 +35,8 @@ public enum Resource { |
35 | 35 | OAUTH2_CONFIGURATION_INFO(), |
36 | 36 | OAUTH2_CONFIGURATION_TEMPLATE(), |
37 | 37 | TENANT_PROFILE(EntityType.TENANT_PROFILE), |
38 | - DEVICE_PROFILE(EntityType.DEVICE_PROFILE); | |
38 | + DEVICE_PROFILE(EntityType.DEVICE_PROFILE), | |
39 | + API_USAGE_STATE(EntityType.API_USAGE_STATE); | |
39 | 40 | |
40 | 41 | private final EntityType entityType; |
41 | 42 | ... | ... |
... | ... | @@ -40,6 +40,7 @@ public class TenantAdminPermissions extends AbstractPermissions { |
40 | 40 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); |
41 | 41 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); |
42 | 42 | put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker); |
43 | + put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker); | |
43 | 44 | } |
44 | 45 | |
45 | 46 | public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { | ... | ... |
... | ... | @@ -40,17 +40,20 @@ import org.thingsboard.rule.engine.api.MailService; |
40 | 40 | import org.thingsboard.server.common.data.AdminSettings; |
41 | 41 | import org.thingsboard.server.common.data.User; |
42 | 42 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
43 | +import org.thingsboard.server.common.data.id.CustomerId; | |
43 | 44 | import org.thingsboard.server.common.data.id.TenantId; |
44 | 45 | import org.thingsboard.server.common.data.security.UserCredentials; |
46 | +import org.thingsboard.server.common.data.security.model.SecuritySettings; | |
47 | +import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; | |
45 | 48 | import org.thingsboard.server.dao.exception.DataValidationException; |
46 | 49 | import org.thingsboard.server.dao.settings.AdminSettingsService; |
47 | 50 | import org.thingsboard.server.dao.user.UserService; |
48 | 51 | import org.thingsboard.server.dao.user.UserServiceImpl; |
49 | 52 | import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; |
50 | -import org.thingsboard.server.common.data.security.model.SecuritySettings; | |
51 | -import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; | |
53 | +import org.thingsboard.server.utils.MiscUtils; | |
52 | 54 | |
53 | 55 | import javax.annotation.Resource; |
56 | +import javax.servlet.http.HttpServletRequest; | |
54 | 57 | import java.util.ArrayList; |
55 | 58 | import java.util.List; |
56 | 59 | import java.util.Map; |
... | ... | @@ -146,7 +149,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
146 | 149 | if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) { |
147 | 150 | if ((userCredentials.getCreatedTime() |
148 | 151 | + TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) |
149 | - < System.currentTimeMillis()) { | |
152 | + < System.currentTimeMillis()) { | |
150 | 153 | userCredentials = userService.requestExpiredPasswordReset(tenantId, userCredentials.getId()); |
151 | 154 | throw new UserPasswordExpiredException("User password expired!", userCredentials.getResetToken()); |
152 | 155 | } |
... | ... | @@ -197,6 +200,21 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
197 | 200 | } |
198 | 201 | } |
199 | 202 | |
203 | + @Override | |
204 | + public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) { | |
205 | + String baseUrl; | |
206 | + AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general"); | |
207 | + | |
208 | + JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); | |
209 | + | |
210 | + if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { | |
211 | + baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); | |
212 | + } else { | |
213 | + baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); | |
214 | + } | |
215 | + return baseUrl; | |
216 | + } | |
217 | + | |
200 | 218 | private static boolean isPositiveInteger(Integer val) { |
201 | 219 | return val != null && val.intValue() > 0; |
202 | 220 | } | ... | ... |
... | ... | @@ -16,11 +16,14 @@ |
16 | 16 | package org.thingsboard.server.service.security.system; |
17 | 17 | |
18 | 18 | import org.springframework.security.core.AuthenticationException; |
19 | +import org.thingsboard.server.common.data.id.CustomerId; | |
19 | 20 | import org.thingsboard.server.common.data.id.TenantId; |
20 | 21 | import org.thingsboard.server.common.data.security.UserCredentials; |
21 | 22 | import org.thingsboard.server.dao.exception.DataValidationException; |
22 | 23 | import org.thingsboard.server.common.data.security.model.SecuritySettings; |
23 | 24 | |
25 | +import javax.servlet.http.HttpServletRequest; | |
26 | + | |
24 | 27 | public interface SystemSecurityService { |
25 | 28 | |
26 | 29 | SecuritySettings getSecuritySettings(TenantId tenantId); |
... | ... | @@ -31,4 +34,6 @@ public interface SystemSecurityService { |
31 | 34 | |
32 | 35 | void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException; |
33 | 36 | |
37 | + String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest); | |
38 | + | |
34 | 39 | } | ... | ... |
... | ... | @@ -48,9 +48,9 @@ import java.util.stream.Collectors; |
48 | 48 | public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { |
49 | 49 | |
50 | 50 | public static final String TB_SERVICE_QUEUE = "TbServiceQueue"; |
51 | - public static final FutureCallback<Void> CALLBACK = new FutureCallback<Void>() { | |
51 | + public static final FutureCallback<Integer> CALLBACK = new FutureCallback<Integer>() { | |
52 | 52 | @Override |
53 | - public void onSuccess(@Nullable Void result) { | |
53 | + public void onSuccess(@Nullable Integer result) { | |
54 | 54 | |
55 | 55 | } |
56 | 56 | |
... | ... | @@ -85,7 +85,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS |
85 | 85 | .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get()))) |
86 | 86 | .collect(Collectors.toList()); |
87 | 87 | if (!tsList.isEmpty()) { |
88 | - tsService.saveAndNotify(tenantId, serviceAssetId, tsList, CALLBACK); | |
88 | + tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, CALLBACK); | |
89 | 89 | } |
90 | 90 | } |
91 | 91 | } catch (DataValidationException e) { |
... | ... | @@ -97,7 +97,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS |
97 | 97 | ruleEngineStats.getTenantExceptions().forEach((tenantId, e) -> { |
98 | 98 | TsKvEntry tsKv = new BasicTsKvEntry(ts, new JsonDataEntry("ruleEngineException", e.toJsonString())); |
99 | 99 | try { |
100 | - tsService.saveAndNotify(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), CALLBACK); | |
100 | + tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), CALLBACK); | |
101 | 101 | } catch (DataValidationException e2) { |
102 | 102 | if (!e2.getMessage().equalsIgnoreCase("Asset is referencing to non-existent tenant!")) { |
103 | 103 | throw e2; | ... | ... |
... | ... | @@ -105,10 +105,10 @@ public abstract class AbstractSubscriptionService implements ApplicationListener |
105 | 105 | } |
106 | 106 | } |
107 | 107 | |
108 | - protected void addWsCallback(ListenableFuture<List<Void>> saveFuture, Consumer<Void> callback) { | |
109 | - Futures.addCallback(saveFuture, new FutureCallback<List<Void>>() { | |
108 | + protected <T> void addWsCallback(ListenableFuture<T> saveFuture, Consumer<T> callback) { | |
109 | + Futures.addCallback(saveFuture, new FutureCallback<T>() { | |
110 | 110 | @Override |
111 | - public void onSuccess(@Nullable List<Void> result) { | |
111 | + public void onSuccess(@Nullable T result) { | |
112 | 112 | callback.accept(null); |
113 | 113 | } |
114 | 114 | ... | ... |
... | ... | @@ -20,10 +20,9 @@ import com.google.common.util.concurrent.Futures; |
20 | 20 | import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import com.google.common.util.concurrent.MoreExecutors; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | -import org.springframework.beans.factory.annotation.Autowired; | |
24 | -import org.springframework.context.event.EventListener; | |
25 | 23 | import org.springframework.stereotype.Service; |
26 | 24 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
25 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
27 | 26 | import org.thingsboard.server.common.data.EntityType; |
28 | 27 | import org.thingsboard.server.common.data.EntityView; |
29 | 28 | import org.thingsboard.server.common.data.id.EntityId; |
... | ... | @@ -41,11 +40,12 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
41 | 40 | import org.thingsboard.server.dao.attributes.AttributesService; |
42 | 41 | import org.thingsboard.server.dao.entityview.EntityViewService; |
43 | 42 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
43 | +import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | |
44 | 44 | import org.thingsboard.server.gen.transport.TransportProtos; |
45 | -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | |
46 | 45 | import org.thingsboard.server.queue.discovery.PartitionService; |
46 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
47 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
47 | 48 | import org.thingsboard.server.service.queue.TbClusterService; |
48 | -import org.thingsboard.server.service.subscription.SubscriptionManagerService; | |
49 | 49 | import org.thingsboard.server.service.subscription.TbSubscriptionUtils; |
50 | 50 | |
51 | 51 | import javax.annotation.Nullable; |
... | ... | @@ -59,12 +59,8 @@ import java.util.HashMap; |
59 | 59 | import java.util.List; |
60 | 60 | import java.util.Map; |
61 | 61 | import java.util.Optional; |
62 | -import java.util.Set; | |
63 | -import java.util.concurrent.ConcurrentHashMap; | |
64 | 62 | import java.util.concurrent.ExecutorService; |
65 | 63 | import java.util.concurrent.Executors; |
66 | -import java.util.function.Consumer; | |
67 | -import java.util.stream.Collectors; | |
68 | 64 | |
69 | 65 | /** |
70 | 66 | * Created by ashvayka on 27.03.18. |
... | ... | @@ -76,6 +72,8 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
76 | 72 | private final AttributesService attrService; |
77 | 73 | private final TimeseriesService tsService; |
78 | 74 | private final EntityViewService entityViewService; |
75 | + private final TbApiUsageClient apiUsageClient; | |
76 | + private final TbApiUsageStateService apiUsageStateService; | |
79 | 77 | |
80 | 78 | private ExecutorService tsCallBackExecutor; |
81 | 79 | |
... | ... | @@ -83,11 +81,15 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
83 | 81 | TimeseriesService tsService, |
84 | 82 | EntityViewService entityViewService, |
85 | 83 | TbClusterService clusterService, |
86 | - PartitionService partitionService) { | |
84 | + PartitionService partitionService, | |
85 | + TbApiUsageClient apiUsageClient, | |
86 | + TbApiUsageStateService apiUsageStateService) { | |
87 | 87 | super(clusterService, partitionService); |
88 | 88 | this.attrService = attrService; |
89 | 89 | this.tsService = tsService; |
90 | 90 | this.entityViewService = entityViewService; |
91 | + this.apiUsageClient = apiUsageClient; | |
92 | + this.apiUsageStateService = apiUsageStateService; | |
91 | 93 | } |
92 | 94 | |
93 | 95 | @PostConstruct |
... | ... | @@ -116,7 +118,35 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
116 | 118 | |
117 | 119 | @Override |
118 | 120 | public void saveAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) { |
119 | - ListenableFuture<List<Void>> saveFuture = tsService.save(tenantId, entityId, ts, ttl); | |
121 | + checkInternalEntity(entityId); | |
122 | + if (apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { | |
123 | + saveAndNotifyInternal(tenantId, entityId, ts, ttl, new FutureCallback<Integer>() { | |
124 | + @Override | |
125 | + public void onSuccess(Integer result) { | |
126 | + if (result != null && result > 0) { | |
127 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.STORAGE_DP_COUNT, result); | |
128 | + } | |
129 | + callback.onSuccess(null); | |
130 | + } | |
131 | + | |
132 | + @Override | |
133 | + public void onFailure(Throwable t) { | |
134 | + callback.onFailure(t); | |
135 | + } | |
136 | + }); | |
137 | + } else{ | |
138 | + callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!")); | |
139 | + } | |
140 | + } | |
141 | + | |
142 | + @Override | |
143 | + public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Integer> callback) { | |
144 | + saveAndNotifyInternal(tenantId, entityId, ts, 0L, callback); | |
145 | + } | |
146 | + | |
147 | + @Override | |
148 | + public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Integer> callback) { | |
149 | + ListenableFuture<Integer> saveFuture = tsService.save(tenantId, entityId, ts, ttl); | |
120 | 150 | addMainCallback(saveFuture, callback); |
121 | 151 | addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); |
122 | 152 | if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { |
... | ... | @@ -141,9 +171,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
141 | 171 | Optional<TsKvEntry> tsKvEntry = entries.stream() |
142 | 172 | .filter(entry -> entry.getTs() > startTs && entry.getTs() <= endTs) |
143 | 173 | .max(Comparator.comparingLong(TsKvEntry::getTs)); |
144 | - if (tsKvEntry.isPresent()) { | |
145 | - entityViewLatest.add(tsKvEntry.get()); | |
146 | - } | |
174 | + tsKvEntry.ifPresent(entityViewLatest::add); | |
147 | 175 | } |
148 | 176 | } |
149 | 177 | if (!entityViewLatest.isEmpty()) { |
... | ... | @@ -176,29 +204,53 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
176 | 204 | |
177 | 205 | @Override |
178 | 206 | public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) { |
207 | + checkInternalEntity(entityId); | |
208 | + saveAndNotifyInternal(tenantId, entityId, scope, attributes, notifyDevice, callback); | |
209 | + } | |
210 | + | |
211 | + @Override | |
212 | + public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) { | |
179 | 213 | ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes); |
180 | - addMainCallback(saveFuture, callback); | |
214 | + addVoidCallback(saveFuture, callback); | |
181 | 215 | addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice)); |
182 | 216 | } |
183 | 217 | |
184 | 218 | @Override |
185 | 219 | public void saveLatestAndNotify(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { |
220 | + checkInternalEntity(entityId); | |
221 | + saveLatestAndNotifyInternal(tenantId, entityId, ts, callback); | |
222 | + } | |
223 | + | |
224 | + @Override | |
225 | + public void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback) { | |
186 | 226 | ListenableFuture<List<Void>> saveFuture = tsService.saveLatest(tenantId, entityId, ts); |
187 | - addMainCallback(saveFuture, callback); | |
227 | + addVoidCallback(saveFuture, callback); | |
188 | 228 | addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); |
189 | 229 | } |
190 | 230 | |
191 | 231 | @Override |
192 | 232 | public void deleteAndNotify(TenantId tenantId, EntityId entityId, String scope, List<String> keys, FutureCallback<Void> callback) { |
233 | + checkInternalEntity(entityId); | |
234 | + deleteAndNotifyInternal(tenantId, entityId, scope, keys, callback); | |
235 | + } | |
236 | + | |
237 | + @Override | |
238 | + public void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<String> keys, FutureCallback<Void> callback) { | |
193 | 239 | ListenableFuture<List<Void>> deleteFuture = attrService.removeAll(tenantId, entityId, scope, keys); |
194 | - addMainCallback(deleteFuture, callback); | |
240 | + addVoidCallback(deleteFuture, callback); | |
195 | 241 | addWsCallback(deleteFuture, success -> onAttributesDelete(tenantId, entityId, scope, keys)); |
196 | 242 | } |
197 | 243 | |
198 | 244 | @Override |
199 | 245 | public void deleteLatest(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback) { |
246 | + checkInternalEntity(entityId); | |
247 | + deleteLatestInternal(tenantId, entityId, keys, callback); | |
248 | + } | |
249 | + | |
250 | + @Override | |
251 | + public void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback) { | |
200 | 252 | ListenableFuture<List<Void>> deleteFuture = tsService.removeLatest(tenantId, entityId, keys); |
201 | - addMainCallback(deleteFuture, callback); | |
253 | + addVoidCallback(deleteFuture, callback); | |
202 | 254 | } |
203 | 255 | |
204 | 256 | @Override |
... | ... | @@ -283,7 +335,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
283 | 335 | } |
284 | 336 | } |
285 | 337 | |
286 | - private <S, R> void addMainCallback(ListenableFuture<S> saveFuture, final FutureCallback<R> callback) { | |
338 | + private <S> void addVoidCallback(ListenableFuture<S> saveFuture, final FutureCallback<Void> callback) { | |
287 | 339 | Futures.addCallback(saveFuture, new FutureCallback<S>() { |
288 | 340 | @Override |
289 | 341 | public void onSuccess(@Nullable S result) { |
... | ... | @@ -296,4 +348,25 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer |
296 | 348 | } |
297 | 349 | }, tsCallBackExecutor); |
298 | 350 | } |
351 | + | |
352 | + private <S> void addMainCallback(ListenableFuture<S> saveFuture, final FutureCallback<S> callback) { | |
353 | + Futures.addCallback(saveFuture, new FutureCallback<S>() { | |
354 | + @Override | |
355 | + public void onSuccess(@Nullable S result) { | |
356 | + callback.onSuccess(result); | |
357 | + } | |
358 | + | |
359 | + @Override | |
360 | + public void onFailure(Throwable t) { | |
361 | + callback.onFailure(t); | |
362 | + } | |
363 | + }, tsCallBackExecutor); | |
364 | + } | |
365 | + | |
366 | + private void checkInternalEntity(EntityId entityId) { | |
367 | + if (EntityType.API_USAGE_STATE.equals(entityId.getEntityType())) { | |
368 | + throw new RuntimeException("Can't update API Usage State!"); | |
369 | + } | |
370 | + } | |
371 | + | |
299 | 372 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/telemetry/InternalTelemetryService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.telemetry; | |
17 | + | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; | |
20 | +import org.thingsboard.server.common.data.id.EntityId; | |
21 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
23 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
24 | + | |
25 | +import java.util.List; | |
26 | + | |
27 | +/** | |
28 | + * Created by ashvayka on 27.03.18. | |
29 | + */ | |
30 | +public interface InternalTelemetryService extends RuleEngineTelemetryService { | |
31 | + | |
32 | + void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Integer> callback); | |
33 | + | |
34 | + void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Integer> callback); | |
35 | + | |
36 | + void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback); | |
37 | + | |
38 | + void saveLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Void> callback); | |
39 | + | |
40 | + void deleteAndNotifyInternal(TenantId tenantId, EntityId entityId, String scope, List<String> keys, FutureCallback<Void> callback); | |
41 | + | |
42 | + void deleteLatestInternal(TenantId tenantId, EntityId entityId, List<String> keys, FutureCallback<Void> callback); | |
43 | + | |
44 | + | |
45 | + | |
46 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetrySubscriptionService.java
... | ... | @@ -22,6 +22,6 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
22 | 22 | /** |
23 | 23 | * Created by ashvayka on 27.03.18. |
24 | 24 | */ |
25 | -public interface TelemetrySubscriptionService extends RuleEngineTelemetryService, ApplicationListener<PartitionChangeEvent> { | |
25 | +public interface TelemetrySubscriptionService extends InternalTelemetryService, ApplicationListener<PartitionChangeEvent> { | |
26 | 26 | |
27 | 27 | } | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import com.google.protobuf.ByteString; |
25 | 25 | import lombok.extern.slf4j.Slf4j; |
26 | 26 | import org.springframework.stereotype.Service; |
27 | 27 | import org.springframework.util.StringUtils; |
28 | +import org.thingsboard.server.common.data.ApiUsageState; | |
28 | 29 | import org.thingsboard.server.common.data.DataConstants; |
29 | 30 | import org.thingsboard.server.common.data.Device; |
30 | 31 | import org.thingsboard.server.common.data.DeviceProfile; |
... | ... | @@ -51,7 +52,6 @@ import org.thingsboard.server.dao.device.DeviceService; |
51 | 52 | import org.thingsboard.server.dao.device.provision.ProvisionRequest; |
52 | 53 | import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
53 | 54 | import org.thingsboard.server.dao.relation.RelationService; |
54 | -import org.thingsboard.server.dao.tenant.TenantService; | |
55 | 55 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
56 | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | 57 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
... | ... | @@ -68,9 +68,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce |
68 | 68 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
69 | 69 | import org.thingsboard.server.queue.util.TbCoreComponent; |
70 | 70 | import org.thingsboard.server.dao.device.provision.ProvisionFailedException; |
71 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
71 | 72 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
72 | 73 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
73 | -import org.thingsboard.server.service.profile.TbTenantProfileCache; | |
74 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
74 | 75 | import org.thingsboard.server.service.queue.TbClusterService; |
75 | 76 | import org.thingsboard.server.service.state.DeviceStateService; |
76 | 77 | |
... | ... | @@ -92,6 +93,7 @@ public class DefaultTransportApiService implements TransportApiService { |
92 | 93 | |
93 | 94 | private final TbDeviceProfileCache deviceProfileCache; |
94 | 95 | private final TbTenantProfileCache tenantProfileCache; |
96 | + private final TbApiUsageStateService apiUsageStateService; | |
95 | 97 | private final DeviceService deviceService; |
96 | 98 | private final RelationService relationService; |
97 | 99 | private final DeviceCredentialsService deviceCredentialsService; |
... | ... | @@ -104,13 +106,14 @@ public class DefaultTransportApiService implements TransportApiService { |
104 | 106 | private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); |
105 | 107 | |
106 | 108 | public DefaultTransportApiService(TbDeviceProfileCache deviceProfileCache, |
107 | - TbTenantProfileCache tenantProfileCache, DeviceService deviceService, | |
109 | + TbTenantProfileCache tenantProfileCache, TbApiUsageStateService apiUsageStateService, DeviceService deviceService, | |
108 | 110 | RelationService relationService, DeviceCredentialsService deviceCredentialsService, |
109 | 111 | DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, |
110 | 112 | TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, |
111 | 113 | DeviceProvisionService deviceProvisionService) { |
112 | 114 | this.deviceProfileCache = deviceProfileCache; |
113 | 115 | this.tenantProfileCache = tenantProfileCache; |
116 | + this.apiUsageStateService = apiUsageStateService; | |
114 | 117 | this.deviceService = deviceService; |
115 | 118 | this.relationService = relationService; |
116 | 119 | this.deviceCredentialsService = deviceCredentialsService; |
... | ... | @@ -316,18 +319,21 @@ public class DefaultTransportApiService implements TransportApiService { |
316 | 319 | private ListenableFuture<TransportApiResponseMsg> handle(GetEntityProfileRequestMsg requestMsg) { |
317 | 320 | EntityType entityType = EntityType.valueOf(requestMsg.getEntityType()); |
318 | 321 | UUID entityUuid = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()); |
319 | - ByteString data; | |
322 | + GetEntityProfileResponseMsg.Builder builder = GetEntityProfileResponseMsg.newBuilder(); | |
320 | 323 | if (entityType.equals(EntityType.DEVICE_PROFILE)) { |
321 | 324 | DeviceProfileId deviceProfileId = new DeviceProfileId(entityUuid); |
322 | 325 | DeviceProfile deviceProfile = deviceProfileCache.find(deviceProfileId); |
323 | - data = ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile)); | |
326 | + builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); | |
324 | 327 | } else if (entityType.equals(EntityType.TENANT)) { |
325 | - TenantProfile tenantProfile = tenantProfileCache.get(new TenantId(entityUuid)); | |
326 | - data = ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile)); | |
328 | + TenantId tenantId = new TenantId(entityUuid); | |
329 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | |
330 | + ApiUsageState state = apiUsageStateService.getApiUsageState(tenantId); | |
331 | + builder.setData(ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile))); | |
332 | + builder.setApiState(ByteString.copyFrom(dataDecodingEncodingService.encode(state))); | |
327 | 333 | } else { |
328 | 334 | throw new RuntimeException("Invalid entity profile request: " + entityType); |
329 | 335 | } |
330 | - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(GetEntityProfileResponseMsg.newBuilder().setData(data).build()).build()); | |
336 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); | |
331 | 337 | } |
332 | 338 | |
333 | 339 | private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Value; |
20 | 20 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
21 | 21 | import org.springframework.context.event.EventListener; |
22 | +import org.springframework.core.annotation.Order; | |
22 | 23 | import org.springframework.stereotype.Service; |
23 | 24 | import org.thingsboard.server.common.stats.MessagesStats; |
24 | 25 | import org.thingsboard.server.common.stats.StatsFactory; |
... | ... | @@ -90,6 +91,7 @@ public class TbCoreTransportApiService { |
90 | 91 | } |
91 | 92 | |
92 | 93 | @EventListener(ApplicationReadyEvent.class) |
94 | + @Order(value = 2) | |
93 | 95 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { |
94 | 96 | log.info("Received application ready event. Starting polling for events."); |
95 | 97 | transportApiTemplate.init(transportApiService); | ... | ... |
... | ... | @@ -673,6 +673,7 @@ queue: |
673 | 673 | poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" |
674 | 674 | partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" |
675 | 675 | pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" |
676 | + usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" | |
676 | 677 | stats: |
677 | 678 | enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" |
678 | 679 | print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}" | ... | ... |
... | ... | @@ -23,7 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.thingsboard.server.common.data.EntityInfo; |
24 | 24 | import org.thingsboard.server.common.data.Tenant; |
25 | 25 | import org.thingsboard.server.common.data.TenantProfile; |
26 | -import org.thingsboard.server.common.data.TenantProfileData; | |
26 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
27 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
27 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 29 | import org.thingsboard.server.common.data.page.PageData; |
29 | 30 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -285,7 +286,9 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController |
285 | 286 | TenantProfile tenantProfile = new TenantProfile(); |
286 | 287 | tenantProfile.setName(name); |
287 | 288 | tenantProfile.setDescription(name + " Test"); |
288 | - tenantProfile.setProfileData(new TenantProfileData()); | |
289 | + TenantProfileData tenantProfileData = new TenantProfileData(); | |
290 | + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); | |
291 | + tenantProfile.setProfileData(tenantProfileData); | |
289 | 292 | tenantProfile.setDefault(false); |
290 | 293 | tenantProfile.setIsolatedTbCore(false); |
291 | 294 | tenantProfile.setIsolatedTbRuleEngine(false); | ... | ... |
... | ... | @@ -27,7 +27,7 @@ import java.util.Arrays; |
27 | 27 | @RunWith(ClasspathSuite.class) |
28 | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | 29 | // "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", |
30 | -// "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest", | |
30 | +// "org.thingsboard.server.controller.sql.TenantProfileControllerSqlTest", | |
31 | 31 | "org.thingsboard.server.controller.sql.*Test", |
32 | 32 | }) |
33 | 33 | public class ControllerSqlTestSuite { | ... | ... |
application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2020 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.script; | |
17 | - | |
18 | -import com.datastax.oss.driver.api.core.uuid.Uuids; | |
19 | -import com.google.common.collect.Sets; | |
20 | -import org.junit.After; | |
21 | -import org.junit.Before; | |
22 | -import org.junit.Test; | |
23 | -import org.thingsboard.rule.engine.api.ScriptEngine; | |
24 | -import org.thingsboard.server.common.data.id.EntityId; | |
25 | -import org.thingsboard.server.common.data.id.RuleNodeId; | |
26 | -import org.thingsboard.server.common.msg.TbMsg; | |
27 | -import org.thingsboard.server.common.msg.TbMsgDataType; | |
28 | -import org.thingsboard.server.common.msg.TbMsgMetaData; | |
29 | - | |
30 | -import javax.script.ScriptException; | |
31 | -import java.util.Map; | |
32 | -import java.util.Set; | |
33 | -import java.util.UUID; | |
34 | -import java.util.concurrent.*; | |
35 | -import java.util.concurrent.atomic.AtomicInteger; | |
36 | - | |
37 | -import static org.junit.Assert.*; | |
38 | - | |
39 | -public class RuleNodeJsScriptEngineTest { | |
40 | - | |
41 | - private ScriptEngine scriptEngine; | |
42 | - private TestNashornJsInvokeService jsSandboxService; | |
43 | - | |
44 | - private EntityId ruleNodeId = new RuleNodeId(Uuids.timeBased()); | |
45 | - | |
46 | - @Before | |
47 | - public void beforeTest() throws Exception { | |
48 | - jsSandboxService = new TestNashornJsInvokeService(false, 1, 100, 3); | |
49 | - } | |
50 | - | |
51 | - @After | |
52 | - public void afterTest() throws Exception { | |
53 | - jsSandboxService.stop(); | |
54 | - } | |
55 | - | |
56 | - @Test | |
57 | - public void msgCanBeUpdated() throws ScriptException { | |
58 | - String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};"; | |
59 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, function); | |
60 | - | |
61 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
62 | - metaData.putValue("temp", "7"); | |
63 | - metaData.putValue("humidity", "99"); | |
64 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
65 | - | |
66 | - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
67 | - | |
68 | - TbMsg actual = scriptEngine.executeUpdate(msg); | |
69 | - assertEquals("70", actual.getMetaData().getValue("temp")); | |
70 | - scriptEngine.destroy(); | |
71 | - } | |
72 | - | |
73 | - @Test | |
74 | - public void newAttributesCanBeAddedInMsg() throws ScriptException { | |
75 | - String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};"; | |
76 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, function); | |
77 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
78 | - metaData.putValue("temp", "7"); | |
79 | - metaData.putValue("humidity", "99"); | |
80 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
81 | - | |
82 | - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
83 | - | |
84 | - TbMsg actual = scriptEngine.executeUpdate(msg); | |
85 | - assertEquals("94", actual.getMetaData().getValue("newAttr")); | |
86 | - scriptEngine.destroy(); | |
87 | - } | |
88 | - | |
89 | - @Test | |
90 | - public void payloadCanBeUpdated() throws ScriptException { | |
91 | - String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};"; | |
92 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, function); | |
93 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
94 | - metaData.putValue("temp", "7"); | |
95 | - metaData.putValue("humidity", "99"); | |
96 | - String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}"; | |
97 | - | |
98 | - TbMsg msg =TbMsg.newMsg("USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
99 | - | |
100 | - TbMsg actual = scriptEngine.executeUpdate(msg); | |
101 | - | |
102 | - String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}"; | |
103 | - assertEquals(expectedJson, actual.getData()); | |
104 | - scriptEngine.destroy(); | |
105 | - } | |
106 | - | |
107 | - @Test | |
108 | - public void metadataAccessibleForFilter() throws ScriptException { | |
109 | - String function = "return metadata.humidity < 15;"; | |
110 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, function); | |
111 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
112 | - metaData.putValue("temp", "7"); | |
113 | - metaData.putValue("humidity", "99"); | |
114 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
115 | - | |
116 | - TbMsg msg = TbMsg.newMsg("USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
117 | - assertFalse(scriptEngine.executeFilter(msg)); | |
118 | - scriptEngine.destroy(); | |
119 | - } | |
120 | - | |
121 | - @Test | |
122 | - public void dataAccessibleForFilter() throws ScriptException { | |
123 | - String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;"; | |
124 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, function); | |
125 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
126 | - metaData.putValue("temp", "7"); | |
127 | - metaData.putValue("humidity", "99"); | |
128 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
129 | - | |
130 | - TbMsg msg = TbMsg.newMsg( "USER", null, metaData,TbMsgDataType.JSON, rawJson); | |
131 | - assertTrue(scriptEngine.executeFilter(msg)); | |
132 | - scriptEngine.destroy(); | |
133 | - } | |
134 | - | |
135 | - @Test | |
136 | - public void dataAccessibleForSwitch() throws ScriptException { | |
137 | - String jsCode = "function nextRelation(metadata, msg) {\n" + | |
138 | - " if(msg.passed == 5 && metadata.temp == 10)\n" + | |
139 | - " return 'one'\n" + | |
140 | - " else\n" + | |
141 | - " return 'two';\n" + | |
142 | - "};\n" + | |
143 | - "\n" + | |
144 | - "return nextRelation(metadata, msg);"; | |
145 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, jsCode); | |
146 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
147 | - metaData.putValue("temp", "10"); | |
148 | - metaData.putValue("humidity", "99"); | |
149 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
150 | - | |
151 | - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
152 | - Set<String> actual = scriptEngine.executeSwitch(msg); | |
153 | - assertEquals(Sets.newHashSet("one"), actual); | |
154 | - scriptEngine.destroy(); | |
155 | - } | |
156 | - | |
157 | - @Test | |
158 | - public void multipleRelationsReturnedFromSwitch() throws ScriptException { | |
159 | - String jsCode = "function nextRelation(metadata, msg) {\n" + | |
160 | - " if(msg.passed == 5 && metadata.temp == 10)\n" + | |
161 | - " return ['three', 'one']\n" + | |
162 | - " else\n" + | |
163 | - " return 'two';\n" + | |
164 | - "};\n" + | |
165 | - "\n" + | |
166 | - "return nextRelation(metadata, msg);"; | |
167 | - scriptEngine = new RuleNodeJsScriptEngine(jsSandboxService, ruleNodeId, jsCode); | |
168 | - TbMsgMetaData metaData = new TbMsgMetaData(); | |
169 | - metaData.putValue("temp", "10"); | |
170 | - metaData.putValue("humidity", "99"); | |
171 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}"; | |
172 | - | |
173 | - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson); | |
174 | - Set<String> actual = scriptEngine.executeSwitch(msg); | |
175 | - assertEquals(Sets.newHashSet("one", "three"), actual); | |
176 | - scriptEngine.destroy(); | |
177 | - } | |
178 | - | |
179 | - @Test | |
180 | - public void concurrentReleasedCorrectly() throws InterruptedException, ExecutionException { | |
181 | - String code = "metadata.temp = metadata.temp * 10; return {metadata: metadata};"; | |
182 | - | |
183 | - int repeat = 1000; | |
184 | - ExecutorService service = Executors.newFixedThreadPool(repeat); | |
185 | - Map<UUID, Object> scriptIds = new ConcurrentHashMap<>(); | |
186 | - CountDownLatch startLatch = new CountDownLatch(repeat); | |
187 | - CountDownLatch finishLatch = new CountDownLatch(repeat); | |
188 | - AtomicInteger failedCount = new AtomicInteger(0); | |
189 | - | |
190 | - for (int i = 0; i < repeat; i++) { | |
191 | - service.submit(() -> runScript(startLatch, finishLatch, failedCount, scriptIds, code)); | |
192 | - } | |
193 | - | |
194 | - finishLatch.await(); | |
195 | - assertTrue(scriptIds.size() == 1); | |
196 | - assertTrue(failedCount.get() == 0); | |
197 | - | |
198 | - CountDownLatch nextStart = new CountDownLatch(repeat); | |
199 | - CountDownLatch nextFinish = new CountDownLatch(repeat); | |
200 | - for (int i = 0; i < repeat; i++) { | |
201 | - service.submit(() -> runScript(nextStart, nextFinish, failedCount, scriptIds, code)); | |
202 | - } | |
203 | - | |
204 | - nextFinish.await(); | |
205 | - assertTrue(scriptIds.size() == 1); | |
206 | - assertTrue(failedCount.get() == 0); | |
207 | - service.shutdownNow(); | |
208 | - } | |
209 | - | |
210 | - @Test | |
211 | - public void concurrentFailedEvaluationShouldThrowException() throws InterruptedException { | |
212 | - String code = "metadata.temp = metadata.temp * 10; urn {metadata: metadata};"; | |
213 | - | |
214 | - int repeat = 10000; | |
215 | - ExecutorService service = Executors.newFixedThreadPool(repeat); | |
216 | - Map<UUID, Object> scriptIds = new ConcurrentHashMap<>(); | |
217 | - CountDownLatch startLatch = new CountDownLatch(repeat); | |
218 | - CountDownLatch finishLatch = new CountDownLatch(repeat); | |
219 | - AtomicInteger failedCount = new AtomicInteger(0); | |
220 | - for (int i = 0; i < repeat; i++) { | |
221 | - service.submit(() -> { | |
222 | - service.submit(() -> runScript(startLatch, finishLatch, failedCount, scriptIds, code)); | |
223 | - }); | |
224 | - } | |
225 | - | |
226 | - finishLatch.await(); | |
227 | - assertTrue(scriptIds.isEmpty()); | |
228 | - assertEquals(repeat, failedCount.get()); | |
229 | - service.shutdownNow(); | |
230 | - } | |
231 | - | |
232 | - private void runScript(CountDownLatch startLatch, CountDownLatch finishLatch, AtomicInteger failedCount, | |
233 | - Map<UUID, Object> scriptIds, String code) { | |
234 | - try { | |
235 | - for (int k = 0; k < 10; k++) { | |
236 | - startLatch.countDown(); | |
237 | - startLatch.await(); | |
238 | - UUID scriptId = jsSandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, code).get(); | |
239 | - scriptIds.put(scriptId, new Object()); | |
240 | - jsSandboxService.invokeFunction(scriptId, "{}", "{}", "TEXT").get(); | |
241 | - jsSandboxService.release(scriptId).get(); | |
242 | - } | |
243 | - } catch (Throwable th) { | |
244 | - failedCount.incrementAndGet(); | |
245 | - } finally { | |
246 | - finishLatch.countDown(); | |
247 | - } | |
248 | - } | |
249 | - | |
250 | -} |
application/src/test/java/org/thingsboard/server/service/script/TestNashornJsInvokeService.java
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2020 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.script; | |
17 | - | |
18 | -public class TestNashornJsInvokeService extends AbstractNashornJsInvokeService { | |
19 | - | |
20 | - private boolean useJsSandbox; | |
21 | - private final int monitorThreadPoolSize; | |
22 | - private final long maxCpuTime; | |
23 | - private final int maxErrors; | |
24 | - | |
25 | - public TestNashornJsInvokeService(boolean useJsSandbox, int monitorThreadPoolSize, long maxCpuTime, int maxErrors) { | |
26 | - this.useJsSandbox = useJsSandbox; | |
27 | - this.monitorThreadPoolSize = monitorThreadPoolSize; | |
28 | - this.maxCpuTime = maxCpuTime; | |
29 | - this.maxErrors = maxErrors; | |
30 | - init(); | |
31 | - } | |
32 | - | |
33 | - @Override | |
34 | - protected boolean useJsSandbox() { | |
35 | - return useJsSandbox; | |
36 | - } | |
37 | - | |
38 | - @Override | |
39 | - protected int getMonitorThreadPoolSize() { | |
40 | - return monitorThreadPoolSize; | |
41 | - } | |
42 | - | |
43 | - @Override | |
44 | - protected long getMaxCpuTime() { | |
45 | - return maxCpuTime; | |
46 | - } | |
47 | - | |
48 | - @Override | |
49 | - protected int getMaxErrors() { | |
50 | - return maxErrors; | |
51 | - } | |
52 | - | |
53 | - @Override | |
54 | - protected long getMaxBlacklistDuration() { | |
55 | - return 100000; | |
56 | - } | |
57 | -} |
... | ... | @@ -31,4 +31,6 @@ public interface RuleNodeStateService { |
31 | 31 | RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState); |
32 | 32 | |
33 | 33 | void removeByRuleNodeId(TenantId tenantId, RuleNodeId selfId); |
34 | + | |
35 | + void removeByRuleNodeIdAndEntityId(TenantId tenantId, RuleNodeId selfId, EntityId entityId); | |
34 | 36 | } | ... | ... |
common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java
renamed from
application/src/main/java/org/thingsboard/server/service/profile/TbTenantProfileCache.java
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.service.profile; | |
16 | +package org.thingsboard.server.dao.tenant; | |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.TenantProfile; |
19 | 19 | import org.thingsboard.server.common.data.id.TenantId; | ... | ... |
... | ... | @@ -36,9 +36,9 @@ public interface TimeseriesService { |
36 | 36 | |
37 | 37 | ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId); |
38 | 38 | |
39 | - ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry); | |
39 | + ListenableFuture<Integer> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry); | |
40 | 40 | |
41 | - ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl); | |
41 | + ListenableFuture<Integer> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl); | |
42 | 42 | |
43 | 43 | ListenableFuture<List<Void>> saveLatest(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry); |
44 | 44 | ... | ... |
common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.dao.usagerecord; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.ApiUsageState; | |
19 | +import org.thingsboard.server.common.data.id.ApiUsageStateId; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | + | |
22 | +public interface ApiUsageStateService { | |
23 | + | |
24 | + ApiUsageState createDefaultApiUsageState(TenantId id); | |
25 | + | |
26 | + ApiUsageState update(ApiUsageState apiUsageState); | |
27 | + | |
28 | + ApiUsageState findTenantApiUsageState(TenantId tenantId); | |
29 | + | |
30 | + void deleteApiUsageStateByTenantId(TenantId tenantId); | |
31 | + | |
32 | + ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id); | |
33 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java
renamed from
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/TransportRateLimitType.java
... | ... | @@ -13,35 +13,21 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.transport.limits; | |
16 | +package org.thingsboard.server.common.data; | |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | 19 | |
20 | -public enum TransportRateLimitType { | |
20 | +public enum ApiFeature { | |
21 | + TRANSPORT("transportApiState"), | |
22 | + DB("dbApiState"), | |
23 | + RE("ruleEngineApiState"), | |
24 | + JS("jsExecutionApiState"); | |
21 | 25 | |
22 | - TENANT_MAX_MSGS("transport.tenant.msg", true, true), | |
23 | - TENANT_TELEMETRY_MSGS("transport.tenant.telemetry", true, true), | |
24 | - TENANT_MAX_DATA_POINTS("transport.tenant.dataPoints", true, false), | |
25 | - DEVICE_MAX_MSGS("transport.device.msg", false, true), | |
26 | - DEVICE_TELEMETRY_MSGS("transport.device.telemetry", false, true), | |
27 | - DEVICE_MAX_DATA_POINTS("transport.device.dataPoints", false, false); | |
28 | - | |
29 | - @Getter | |
30 | - private final String configurationKey; | |
31 | - @Getter | |
32 | - private final boolean tenantLevel; | |
33 | 26 | @Getter |
34 | - private final boolean deviceLevel; | |
35 | - @Getter | |
36 | - private final boolean messageLevel; | |
37 | - @Getter | |
38 | - private final boolean dataPointLevel; | |
27 | + private final String apiStateKey; | |
39 | 28 | |
40 | - TransportRateLimitType(String configurationKey, boolean tenantLevel, boolean messageLevel) { | |
41 | - this.configurationKey = configurationKey; | |
42 | - this.tenantLevel = tenantLevel; | |
43 | - this.deviceLevel = !tenantLevel; | |
44 | - this.messageLevel = messageLevel; | |
45 | - this.dataPointLevel = !messageLevel; | |
29 | + ApiFeature(String apiStateKey) { | |
30 | + this.apiStateKey = apiStateKey; | |
46 | 31 | } |
32 | + | |
47 | 33 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +public enum ApiUsageRecordKey { | |
21 | + | |
22 | + TRANSPORT_MSG_COUNT(ApiFeature.TRANSPORT, "transportMsgCount", "transportMsgLimit"), | |
23 | + TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"), | |
24 | + STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"), | |
25 | + RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"), | |
26 | + JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"); | |
27 | + private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT}; | |
28 | + private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT}; | |
29 | + private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT}; | |
30 | + private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT}; | |
31 | + | |
32 | + @Getter | |
33 | + private final ApiFeature apiFeature; | |
34 | + @Getter | |
35 | + private final String apiCountKey; | |
36 | + @Getter | |
37 | + private final String apiLimitKey; | |
38 | + | |
39 | + ApiUsageRecordKey(ApiFeature apiFeature, String apiCountKey, String apiLimitKey) { | |
40 | + this.apiFeature = apiFeature; | |
41 | + this.apiCountKey = apiCountKey; | |
42 | + this.apiLimitKey = apiLimitKey; | |
43 | + } | |
44 | + | |
45 | + public static ApiUsageRecordKey[] getKeys(ApiFeature feature) { | |
46 | + switch (feature) { | |
47 | + case TRANSPORT: | |
48 | + return TRANSPORT_RECORD_KEYS; | |
49 | + case DB: | |
50 | + return DB_RECORD_KEYS; | |
51 | + case RE: | |
52 | + return RE_RECORD_KEYS; | |
53 | + case JS: | |
54 | + return JS_RECORD_KEYS; | |
55 | + default: | |
56 | + return new ApiUsageRecordKey[]{}; | |
57 | + } | |
58 | + } | |
59 | + | |
60 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data; | |
17 | + | |
18 | +import lombok.EqualsAndHashCode; | |
19 | +import lombok.Getter; | |
20 | +import lombok.Setter; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.id.EntityId; | |
23 | +import org.thingsboard.server.common.data.id.TenantId; | |
24 | +import org.thingsboard.server.common.data.id.ApiUsageStateId; | |
25 | + | |
26 | +@ToString | |
27 | +@EqualsAndHashCode(callSuper = true) | |
28 | +public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenantId { | |
29 | + | |
30 | + private static final long serialVersionUID = 8250339805336035966L; | |
31 | + | |
32 | + @Getter | |
33 | + @Setter | |
34 | + private TenantId tenantId; | |
35 | + @Getter | |
36 | + @Setter | |
37 | + private EntityId entityId; | |
38 | + @Getter | |
39 | + @Setter | |
40 | + private ApiUsageStateValue transportState; | |
41 | + @Getter | |
42 | + @Setter | |
43 | + private ApiUsageStateValue dbStorageState; | |
44 | + @Getter | |
45 | + @Setter | |
46 | + private ApiUsageStateValue reExecState; | |
47 | + @Getter | |
48 | + @Setter | |
49 | + private ApiUsageStateValue jsExecState; | |
50 | + | |
51 | + public ApiUsageState() { | |
52 | + super(); | |
53 | + } | |
54 | + | |
55 | + public ApiUsageState(ApiUsageStateId id) { | |
56 | + super(id); | |
57 | + } | |
58 | + | |
59 | + public ApiUsageState(ApiUsageState ur) { | |
60 | + super(ur); | |
61 | + this.tenantId = ur.getTenantId(); | |
62 | + this.entityId = ur.getEntityId(); | |
63 | + this.transportState = ur.getTransportState(); | |
64 | + this.dbStorageState = ur.getDbStorageState(); | |
65 | + this.reExecState = ur.getReExecState(); | |
66 | + this.jsExecState = ur.getJsExecState(); | |
67 | + } | |
68 | + | |
69 | + public boolean isTransportEnabled() { | |
70 | + return !ApiUsageStateValue.DISABLED.equals(transportState); | |
71 | + } | |
72 | + | |
73 | + public boolean isReExecEnabled() { | |
74 | + return !ApiUsageStateValue.DISABLED.equals(reExecState); | |
75 | + } | |
76 | + | |
77 | + public boolean isDbStorageEnabled() { | |
78 | + return !ApiUsageStateValue.DISABLED.equals(dbStorageState); | |
79 | + } | |
80 | + | |
81 | + public boolean isJsExecEnabled() { | |
82 | + return !ApiUsageStateValue.DISABLED.equals(jsExecState); | |
83 | + } | |
84 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data; | |
17 | + | |
18 | +public enum ApiUsageStateValue { | |
19 | + | |
20 | + ENABLED, WARNING, DISABLED; | |
21 | + | |
22 | + | |
23 | + public static ApiUsageStateValue toMoreRestricted(ApiUsageStateValue a, ApiUsageStateValue b) { | |
24 | + return a.ordinal() > b.ordinal() ? a : b; | |
25 | + } | |
26 | +} | ... | ... |
... | ... | @@ -43,6 +43,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H |
43 | 43 | private DeviceTransportType transportType; |
44 | 44 | private DeviceProfileProvisionType provisionType; |
45 | 45 | private RuleChainId defaultRuleChainId; |
46 | + private String defaultQueueName; | |
46 | 47 | private transient DeviceProfileData profileData; |
47 | 48 | @JsonIgnore |
48 | 49 | private byte[] profileDataBytes; |
... | ... | @@ -63,6 +64,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H |
63 | 64 | this.description = deviceProfile.getDescription(); |
64 | 65 | this.isDefault = deviceProfile.isDefault(); |
65 | 66 | this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); |
67 | + this.defaultQueueName = deviceProfile.getDefaultQueueName(); | |
66 | 68 | this.setProfileData(deviceProfile.getProfileData()); |
67 | 69 | this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); |
68 | 70 | } | ... | ... |
... | ... | @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; |
19 | 19 | * @author Andrew Shvayka |
20 | 20 | */ |
21 | 21 | public enum EntityType { |
22 | - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE | |
22 | + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE; | |
23 | 23 | } | ... | ... |
... | ... | @@ -21,6 +21,8 @@ import lombok.Data; |
21 | 21 | import lombok.EqualsAndHashCode; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantProfileId; |
24 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
25 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
24 | 26 | |
25 | 27 | import java.io.ByteArrayInputStream; |
26 | 28 | import java.io.IOException; |
... | ... | @@ -78,15 +80,21 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H |
78 | 80 | profileData = mapper.readValue(new ByteArrayInputStream(profileDataBytes), TenantProfileData.class); |
79 | 81 | } catch (IOException e) { |
80 | 82 | log.warn("Can't deserialize tenant profile data: ", e); |
81 | - return null; | |
83 | + return createDefaultTenantProfileData(); | |
82 | 84 | } |
83 | 85 | return profileData; |
84 | 86 | } else { |
85 | - return null; | |
87 | + return createDefaultTenantProfileData(); | |
86 | 88 | } |
87 | 89 | } |
88 | 90 | } |
89 | 91 | |
92 | + public TenantProfileData createDefaultTenantProfileData() { | |
93 | + TenantProfileData tpd = new TenantProfileData(); | |
94 | + tpd.setConfiguration(new DefaultTenantProfileConfiguration()); | |
95 | + return tpd; | |
96 | + } | |
97 | + | |
90 | 98 | public void setProfileData(TenantProfileData data) { |
91 | 99 | this.profileData = data; |
92 | 100 | try { | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileType.java
renamed from
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/TransportRateLimitFactory.java
... | ... | @@ -13,12 +13,8 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.transport.limits; | |
17 | - | |
18 | -public interface TransportRateLimitFactory { | |
19 | - | |
20 | - TransportRateLimit create(TransportRateLimitType type, Object config); | |
21 | - | |
22 | - TransportRateLimit createDefault(TransportRateLimitType type); | |
16 | +package org.thingsboard.server.common.data; | |
23 | 17 | |
18 | +public enum TenantProfileType { | |
19 | + DEFAULT | |
24 | 20 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.device.profile; |
18 | 18 | import lombok.Data; |
19 | 19 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
20 | 20 | |
21 | +import java.util.LinkedHashMap; | |
21 | 22 | import java.util.List; |
22 | 23 | import java.util.Map; |
23 | 24 | |
... | ... | @@ -27,7 +28,7 @@ public class DeviceProfileAlarm { |
27 | 28 | private String id; |
28 | 29 | private String alarmType; |
29 | 30 | |
30 | - private Map<AlarmSeverity, AlarmRule> createRules; | |
31 | + private LinkedHashMap<AlarmSeverity, AlarmRule> createRules; | |
31 | 32 | private AlarmRule clearRule; |
32 | 33 | |
33 | 34 | // Hidden in advanced settings | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
20 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
21 | +import org.thingsboard.server.common.data.EntityType; | |
22 | + | |
23 | +import java.util.UUID; | |
24 | + | |
25 | +public class ApiUsageStateId extends UUIDBased implements EntityId { | |
26 | + | |
27 | + @JsonCreator | |
28 | + public ApiUsageStateId(@JsonProperty("id") UUID id) { | |
29 | + super(id); | |
30 | + } | |
31 | + | |
32 | + public static ApiUsageStateId fromString(String userId) { | |
33 | + return new ApiUsageStateId(UUID.fromString(userId)); | |
34 | + } | |
35 | + | |
36 | + @JsonIgnore | |
37 | + @Override | |
38 | + public EntityType getEntityType() { | |
39 | + return EntityType.API_USAGE_STATE; | |
40 | + } | |
41 | + | |
42 | +} | ... | ... |
... | ... | @@ -66,6 +66,8 @@ public class EntityIdFactory { |
66 | 66 | return new DeviceProfileId(uuid); |
67 | 67 | case TENANT_PROFILE: |
68 | 68 | return new TenantProfileId(uuid); |
69 | + case API_USAGE_STATE: | |
70 | + return new ApiUsageStateId(uuid); | |
69 | 71 | } |
70 | 72 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); |
71 | 73 | } | ... | ... |
... | ... | @@ -19,7 +19,7 @@ import java.util.Objects; |
19 | 19 | import java.util.Optional; |
20 | 20 | |
21 | 21 | public class BasicTsKvEntry implements TsKvEntry { |
22 | - | |
22 | + private static final int MAX_CHARS_PER_DATA_POINT = 512; | |
23 | 23 | private final long ts; |
24 | 24 | private final KvEntry kv; |
25 | 25 | |
... | ... | @@ -99,4 +99,21 @@ public class BasicTsKvEntry implements TsKvEntry { |
99 | 99 | public String getValueAsString() { |
100 | 100 | return kv.getValueAsString(); |
101 | 101 | } |
102 | + | |
103 | + @Override | |
104 | + public int getDataPoints() { | |
105 | + int length; | |
106 | + switch (getDataType()) { | |
107 | + case STRING: | |
108 | + length = getStrValue().get().length(); | |
109 | + break; | |
110 | + case JSON: | |
111 | + length = getJsonValue().get().length(); | |
112 | + break; | |
113 | + default: | |
114 | + return 1; | |
115 | + } | |
116 | + return Math.max(1, (length + MAX_CHARS_PER_DATA_POINT - 1) / MAX_CHARS_PER_DATA_POINT); | |
117 | + } | |
118 | + | |
102 | 119 | } | ... | ... |
... | ... | @@ -15,6 +15,8 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.kv; |
17 | 17 | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | + | |
18 | 20 | /** |
19 | 21 | * Represents time series KV data entry |
20 | 22 | * |
... | ... | @@ -25,4 +27,7 @@ public interface TsKvEntry extends KvEntry { |
25 | 27 | |
26 | 28 | long getTs(); |
27 | 29 | |
30 | + @JsonIgnore | |
31 | + int getDataPoints(); | |
32 | + | |
28 | 33 | } | ... | ... |
... | ... | @@ -21,5 +21,5 @@ import java.io.Serializable; |
21 | 21 | * @author Andrew Shvayka |
22 | 22 | */ |
23 | 23 | public enum ComponentLifecycleEvent implements Serializable { |
24 | - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED | |
24 | + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, ADDED_TO_ALLOW_LIST, ADDED_TO_DENY_LIST | |
25 | 25 | } |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/query/ApiUsageStateFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data.query; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.id.EntityId; | |
20 | + | |
21 | +@Data | |
22 | +public class ApiUsageStateFilter implements EntityFilter { | |
23 | + @Override | |
24 | + public EntityFilterType getType() { | |
25 | + return EntityFilterType.API_USAGE_STATE; | |
26 | + } | |
27 | + | |
28 | +} | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; |
32 | 32 | @JsonSubTypes.Type(value = AssetTypeFilter.class, name = "assetType"), |
33 | 33 | @JsonSubTypes.Type(value = DeviceTypeFilter.class, name = "deviceType"), |
34 | 34 | @JsonSubTypes.Type(value = EntityViewTypeFilter.class, name = "entityViewType"), |
35 | + @JsonSubTypes.Type(value = ApiUsageStateFilter.class, name = "apiUsageState"), | |
35 | 36 | @JsonSubTypes.Type(value = RelationsQueryFilter.class, name = "relationsQuery"), |
36 | 37 | @JsonSubTypes.Type(value = AssetSearchQueryFilter.class, name = "assetSearchQuery"), |
37 | 38 | @JsonSubTypes.Type(value = DeviceSearchQueryFilter.class, name = "deviceSearchQuery"), | ... | ... |
... | ... | @@ -25,7 +25,8 @@ public enum EntityFilterType { |
25 | 25 | RELATIONS_QUERY("relationsQuery"), |
26 | 26 | ASSET_SEARCH_QUERY("assetSearchQuery"), |
27 | 27 | DEVICE_SEARCH_QUERY("deviceSearchQuery"), |
28 | - ENTITY_VIEW_SEARCH_QUERY("entityViewSearchQuery"); | |
28 | + ENTITY_VIEW_SEARCH_QUERY("entityViewSearchQuery"), | |
29 | + API_USAGE_STATE("apiUsageState"); | |
29 | 30 | |
30 | 31 | private final String label; |
31 | 32 | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data.tenant.profile; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
20 | +import org.thingsboard.server.common.data.TenantProfileType; | |
21 | + | |
22 | +@Data | |
23 | +public class DefaultTenantProfileConfiguration implements TenantProfileConfiguration { | |
24 | + | |
25 | + private long maxDevices; | |
26 | + private long maxAssets; | |
27 | + | |
28 | + private String transportTenantMsgRateLimit; | |
29 | + private String transportTenantTelemetryMsgRateLimit; | |
30 | + private String transportTenantTelemetryDataPointsRateLimit; | |
31 | + private String transportDeviceMsgRateLimit; | |
32 | + private String transportDeviceTelemetryMsgRateLimit; | |
33 | + private String transportDeviceTelemetryDataPointsRateLimit; | |
34 | + | |
35 | + private long maxTransportMessages; | |
36 | + private long maxTransportDataPoints; | |
37 | + private long maxREExecutions; | |
38 | + private long maxJSExecutions; | |
39 | + private long maxDPStorageDays; | |
40 | + private int maxRuleNodeExecutionsPerMessage; | |
41 | + | |
42 | + private double warnThreshold; | |
43 | + | |
44 | + @Override | |
45 | + public long getProfileThreshold(ApiUsageRecordKey key) { | |
46 | + switch (key) { | |
47 | + case TRANSPORT_MSG_COUNT: | |
48 | + return maxTransportMessages; | |
49 | + case TRANSPORT_DP_COUNT: | |
50 | + return maxTransportDataPoints; | |
51 | + case JS_EXEC_COUNT: | |
52 | + return maxJSExecutions; | |
53 | + case RE_EXEC_COUNT: | |
54 | + return maxREExecutions; | |
55 | + case STORAGE_DP_COUNT: | |
56 | + return maxDPStorageDays; | |
57 | + } | |
58 | + return 0L; | |
59 | + } | |
60 | + | |
61 | + @Override | |
62 | + public long getWarnThreshold(ApiUsageRecordKey key) { | |
63 | + return (long) (getProfileThreshold(key) * (warnThreshold > 0.0 ? warnThreshold : 0.8)); | |
64 | + } | |
65 | + | |
66 | + @Override | |
67 | + public TenantProfileType getType() { | |
68 | + return TenantProfileType.DEFAULT; | |
69 | + } | |
70 | + | |
71 | + @Override | |
72 | + public int getMaxRuleNodeExecsPerMessage() { | |
73 | + return maxRuleNodeExecutionsPerMessage; | |
74 | + } | |
75 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2020 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.data.tenant.profile; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | |
21 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
22 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
23 | +import org.thingsboard.server.common.data.TenantProfileType; | |
24 | + | |
25 | +@JsonIgnoreProperties(ignoreUnknown = true) | |
26 | +@JsonTypeInfo( | |
27 | + use = JsonTypeInfo.Id.NAME, | |
28 | + include = JsonTypeInfo.As.PROPERTY, | |
29 | + property = "type") | |
30 | +@JsonSubTypes({ | |
31 | + @JsonSubTypes.Type(value = DefaultTenantProfileConfiguration.class, name = "DEFAULT")}) | |
32 | +public interface TenantProfileConfiguration { | |
33 | + | |
34 | + @JsonIgnore | |
35 | + TenantProfileType getType(); | |
36 | + | |
37 | + @JsonIgnore | |
38 | + long getProfileThreshold(ApiUsageRecordKey key); | |
39 | + | |
40 | + @JsonIgnore | |
41 | + long getWarnThreshold(ApiUsageRecordKey key); | |
42 | + | |
43 | + @JsonIgnore | |
44 | + int getMaxRuleNodeExecsPerMessage(); | |
45 | + | |
46 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/TenantProfileData.java
... | ... | @@ -13,30 +13,13 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data; | |
16 | +package org.thingsboard.server.common.data.tenant.profile; | |
17 | 17 | |
18 | -import com.fasterxml.jackson.annotation.JsonAnyGetter; | |
19 | -import com.fasterxml.jackson.annotation.JsonAnySetter; | |
20 | -import com.fasterxml.jackson.annotation.JsonIgnore; | |
21 | 18 | import lombok.Data; |
22 | 19 | |
23 | -import java.util.HashMap; | |
24 | -import java.util.Map; | |
25 | - | |
26 | 20 | @Data |
27 | 21 | public class TenantProfileData { |
28 | 22 | |
29 | - @JsonIgnore | |
30 | - private Map<String, Object> properties = new HashMap<>(); | |
31 | - | |
32 | - @JsonAnyGetter | |
33 | - public Map<String, Object> properties() { | |
34 | - return this.properties; | |
35 | - } | |
36 | - | |
37 | - @JsonAnySetter | |
38 | - public void put(String name, Object value) { | |
39 | - this.properties.put(name, value); | |
40 | - } | |
23 | + private TenantProfileConfiguration configuration; | |
41 | 24 | |
42 | 25 | } | ... | ... |
... | ... | @@ -18,27 +18,27 @@ package org.thingsboard.server.common.msg; |
18 | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; |
19 | 19 | import com.google.protobuf.ByteString; |
20 | 20 | import com.google.protobuf.InvalidProtocolBufferException; |
21 | +import lombok.AccessLevel; | |
21 | 22 | import lombok.Builder; |
22 | 23 | import lombok.Data; |
24 | +import lombok.Getter; | |
23 | 25 | import lombok.extern.slf4j.Slf4j; |
24 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
25 | 27 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
26 | 28 | import org.thingsboard.server.common.data.id.RuleChainId; |
27 | 29 | import org.thingsboard.server.common.data.id.RuleNodeId; |
28 | 30 | import org.thingsboard.server.common.msg.gen.MsgProtos; |
29 | -import org.thingsboard.server.common.msg.queue.RuleNodeInfo; | |
30 | 31 | import org.thingsboard.server.common.msg.queue.ServiceQueue; |
31 | 32 | import org.thingsboard.server.common.msg.queue.TbMsgCallback; |
32 | 33 | |
33 | -import java.io.IOException; | |
34 | 34 | import java.io.Serializable; |
35 | 35 | import java.util.UUID; |
36 | +import java.util.concurrent.atomic.AtomicInteger; | |
36 | 37 | |
37 | 38 | /** |
38 | 39 | * Created by ashvayka on 13.01.18. |
39 | 40 | */ |
40 | 41 | @Data |
41 | -@Builder | |
42 | 42 | @Slf4j |
43 | 43 | public final class TbMsg implements Serializable { |
44 | 44 | |
... | ... | @@ -52,51 +52,73 @@ public final class TbMsg implements Serializable { |
52 | 52 | private final String data; |
53 | 53 | private final RuleChainId ruleChainId; |
54 | 54 | private final RuleNodeId ruleNodeId; |
55 | + @Getter(value = AccessLevel.NONE) | |
56 | + private final AtomicInteger ruleNodeExecCounter; | |
57 | + | |
58 | + public int getAndIncrementRuleNodeCounter() { | |
59 | + return ruleNodeExecCounter.getAndIncrement(); | |
60 | + } | |
61 | + | |
55 | 62 | //This field is not serialized because we use queues and there is no need to do it |
56 | 63 | @JsonIgnore |
57 | 64 | transient private final TbMsgCallback callback; |
58 | 65 | |
59 | 66 | public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { |
60 | - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); | |
67 | + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, | |
68 | + metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); | |
61 | 69 | } |
62 | 70 | |
63 | 71 | public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { |
64 | - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY); | |
72 | + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); | |
65 | 73 | } |
66 | 74 | |
75 | + // REALLY NEW MSG | |
76 | + | |
67 | 77 | public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { |
68 | - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, TbMsgCallback.EMPTY); | |
78 | + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, TbMsgCallback.EMPTY); | |
69 | 79 | } |
70 | 80 | |
71 | 81 | public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { |
72 | - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, TbMsgCallback.EMPTY); | |
82 | + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, null, null, 0, TbMsgCallback.EMPTY); | |
73 | 83 | } |
74 | 84 | |
85 | + // For Tests only | |
86 | + | |
75 | 87 | public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { |
76 | - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); | |
88 | + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, 0, TbMsgCallback.EMPTY); | |
77 | 89 | } |
78 | 90 | |
79 | 91 | public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { |
80 | - return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, callback); | |
92 | + return new TbMsg(ServiceQueue.MAIN, UUID.randomUUID(), System.currentTimeMillis(), type, originator, metaData.copy(), TbMsgDataType.JSON, data, null, null, 0, callback); | |
93 | + } | |
94 | + | |
95 | + public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { | |
96 | + return new TbMsg(tbMsg.getQueueName(), tbMsg.getId(), tbMsg.getTs(), type, originator, metaData.copy(), tbMsg.getDataType(), | |
97 | + data, tbMsg.getRuleChainId(), tbMsg.getRuleNodeId(), tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); | |
98 | + } | |
99 | + | |
100 | + public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { | |
101 | + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, | |
102 | + tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); | |
81 | 103 | } |
82 | 104 | |
83 | - public static TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { | |
84 | - return new TbMsg(origMsg.getQueueName(), origMsg.getId(), origMsg.getTs(), type, originator, metaData.copy(), origMsg.getDataType(), | |
85 | - data, origMsg.getRuleChainId(), origMsg.getRuleNodeId(), origMsg.getCallback()); | |
105 | + public static TbMsg transformMsg(TbMsg tbMsg, String queueName) { | |
106 | + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, | |
107 | + tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); | |
86 | 108 | } |
87 | 109 | |
88 | - public static TbMsg transformMsg(TbMsg origMsg, RuleChainId ruleChainId) { | |
89 | - return new TbMsg(origMsg.queueName, origMsg.id, origMsg.ts, origMsg.type, origMsg.originator, origMsg.metaData, origMsg.dataType, | |
90 | - origMsg.data, ruleChainId, null, origMsg.getCallback()); | |
110 | + public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { | |
111 | + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.metaData, tbMsg.dataType, | |
112 | + tbMsg.data, ruleChainId, null, tbMsg.ruleNodeExecCounter.get(), tbMsg.getCallback()); | |
91 | 113 | } |
92 | 114 | |
93 | 115 | public static TbMsg newMsg(TbMsg tbMsg, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { |
94 | 116 | return new TbMsg(tbMsg.getQueueName(), UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), |
95 | - tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, TbMsgCallback.EMPTY); | |
117 | + tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ruleNodeExecCounter.get(), TbMsgCallback.EMPTY); | |
96 | 118 | } |
97 | 119 | |
98 | 120 | private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, |
99 | - RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgCallback callback) { | |
121 | + RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) { | |
100 | 122 | this.id = id; |
101 | 123 | this.queueName = queueName; |
102 | 124 | if (ts > 0) { |
... | ... | @@ -111,6 +133,7 @@ public final class TbMsg implements Serializable { |
111 | 133 | this.data = data; |
112 | 134 | this.ruleChainId = ruleChainId; |
113 | 135 | this.ruleNodeId = ruleNodeId; |
136 | + this.ruleNodeExecCounter = new AtomicInteger(ruleNodeExecCounter); | |
114 | 137 | if (callback != null) { |
115 | 138 | this.callback = callback; |
116 | 139 | } else { |
... | ... | @@ -147,6 +170,7 @@ public final class TbMsg implements Serializable { |
147 | 170 | |
148 | 171 | builder.setDataType(msg.getDataType().ordinal()); |
149 | 172 | builder.setData(msg.getData()); |
173 | + builder.setRuleNodeExecCounter(msg.ruleNodeExecCounter.get()); | |
150 | 174 | return builder.build().toByteArray(); |
151 | 175 | } |
152 | 176 | |
... | ... | @@ -164,18 +188,18 @@ public final class TbMsg implements Serializable { |
164 | 188 | ruleNodeId = new RuleNodeId(new UUID(proto.getRuleNodeIdMSB(), proto.getRuleNodeIdLSB())); |
165 | 189 | } |
166 | 190 | TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; |
167 | - return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, callback); | |
191 | + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, proto.getRuleNodeExecCounter(), callback); | |
168 | 192 | } catch (InvalidProtocolBufferException e) { |
169 | 193 | throw new IllegalStateException("Could not parse protobuf for TbMsg", e); |
170 | 194 | } |
171 | 195 | } |
172 | 196 | |
173 | 197 | public TbMsg copyWithRuleChainId(RuleChainId ruleChainId) { |
174 | - return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, callback); | |
198 | + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, null, this.ruleNodeExecCounter.get(), callback); | |
175 | 199 | } |
176 | 200 | |
177 | 201 | public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId) { |
178 | - return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, callback); | |
202 | + return new TbMsg(this.queueName, this.id, this.ts, this.type, this.originator, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ruleNodeExecCounter.get(), callback); | |
179 | 203 | } |
180 | 204 | |
181 | 205 | public TbMsgCallback getCallback() { | ... | ... |
... | ... | @@ -50,6 +50,10 @@ public class TopicPartitionInfo { |
50 | 50 | this.fullTopicName = tmp; |
51 | 51 | } |
52 | 52 | |
53 | + public TopicPartitionInfo newByTopic(String topic) { | |
54 | + return new TopicPartitionInfo(topic, this.tenantId, this.partition, this.myPartition); | |
55 | + } | |
56 | + | |
53 | 57 | public String getTopic() { |
54 | 58 | return topic; |
55 | 59 | } | ... | ... |
... | ... | @@ -15,16 +15,64 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.msg.tools; |
17 | 17 | |
18 | +import java.time.LocalDate; | |
19 | +import java.time.LocalDateTime; | |
20 | +import java.time.LocalTime; | |
18 | 21 | import java.time.ZoneId; |
22 | +import java.time.ZoneOffset; | |
23 | +import java.time.temporal.ChronoUnit; | |
24 | +import java.time.temporal.TemporalAdjuster; | |
25 | +import java.time.temporal.TemporalAdjusters; | |
26 | +import java.time.temporal.TemporalUnit; | |
19 | 27 | import java.util.concurrent.ConcurrentHashMap; |
20 | 28 | import java.util.concurrent.ConcurrentMap; |
21 | 29 | |
30 | +import static java.time.temporal.ChronoField.DAY_OF_MONTH; | |
31 | +import static java.time.temporal.ChronoUnit.MONTHS; | |
32 | + | |
22 | 33 | public class SchedulerUtils { |
23 | 34 | |
35 | + private final static ZoneId UTC = ZoneId.of("UTC"); | |
24 | 36 | private static final ConcurrentMap<String, ZoneId> tzMap = new ConcurrentHashMap<>(); |
25 | 37 | |
26 | 38 | public static ZoneId getZoneId(String tz) { |
27 | 39 | return tzMap.computeIfAbsent(tz == null || tz.isEmpty() ? "UTC" : tz, ZoneId::of); |
28 | 40 | } |
29 | 41 | |
42 | + public static long getStartOfCurrentHour() { | |
43 | + return getStartOfCurrentHour(UTC); | |
44 | + } | |
45 | + | |
46 | + public static long getStartOfCurrentHour(ZoneId zoneId) { | |
47 | + return LocalDateTime.now(ZoneOffset.UTC).atZone(zoneId).truncatedTo(ChronoUnit.HOURS).toInstant().toEpochMilli(); | |
48 | + } | |
49 | + | |
50 | + public static long getStartOfCurrentMonth() { | |
51 | + return getStartOfCurrentMonth(UTC); | |
52 | + } | |
53 | + | |
54 | + public static long getStartOfCurrentMonth(ZoneId zoneId) { | |
55 | + return LocalDate.now().withDayOfMonth(1).atStartOfDay(zoneId).toInstant().toEpochMilli(); | |
56 | + } | |
57 | + | |
58 | + public static long getStartOfNextMonth() { | |
59 | + return getStartOfNextMonth(UTC); | |
60 | + } | |
61 | + | |
62 | + public static long getStartOfNextMonth(ZoneId zoneId) { | |
63 | + return LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth()).atStartOfDay(zoneId).toInstant().toEpochMilli(); | |
64 | + } | |
65 | + | |
66 | + public static long getStartOfNextNextMonth() { | |
67 | + return getStartOfNextNextMonth(UTC); | |
68 | + } | |
69 | + | |
70 | + public static long getStartOfNextNextMonth(ZoneId zoneId) { | |
71 | + return LocalDate.now().with(firstDayOfNextNextMonth()).atStartOfDay(zoneId).toInstant().toEpochMilli(); | |
72 | + } | |
73 | + | |
74 | + public static TemporalAdjuster firstDayOfNextNextMonth() { | |
75 | + return (temporal) -> temporal.with(DAY_OF_MONTH, 1).plus(2, MONTHS); | |
76 | + } | |
77 | + | |
30 | 78 | } | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java
renamed from
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitFactory.java
... | ... | @@ -13,35 +13,33 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.transport.limits; | |
16 | +package org.thingsboard.server.queue.common; | |
17 | 17 | |
18 | -import lombok.extern.slf4j.Slf4j; | |
19 | -import org.springframework.stereotype.Component; | |
20 | -import org.springframework.util.StringUtils; | |
21 | -import org.thingsboard.server.common.msg.tools.TbRateLimits; | |
18 | +import org.thingsboard.server.common.msg.queue.RuleEngineException; | |
19 | +import org.thingsboard.server.queue.TbQueueCallback; | |
20 | +import org.thingsboard.server.queue.TbQueueMsgMetadata; | |
22 | 21 | |
23 | -@Slf4j | |
24 | -@Component | |
25 | -public class DefaultTransportRateLimitFactory implements TransportRateLimitFactory { | |
22 | +import java.util.concurrent.atomic.AtomicInteger; | |
26 | 23 | |
27 | - private static final DummyTransportRateLimit ALWAYS_TRUE = new DummyTransportRateLimit(); | |
24 | +public class MultipleTbQueueCallbackWrapper implements TbQueueCallback { | |
25 | + | |
26 | + private final AtomicInteger tbQueueCallbackCount; | |
27 | + private final TbQueueCallback callback; | |
28 | + | |
29 | + public MultipleTbQueueCallbackWrapper(int tbQueueCallbackCount, TbQueueCallback callback) { | |
30 | + this.tbQueueCallbackCount = new AtomicInteger(tbQueueCallbackCount); | |
31 | + this.callback = callback; | |
32 | + } | |
28 | 33 | |
29 | 34 | @Override |
30 | - public TransportRateLimit create(TransportRateLimitType type, Object configuration) { | |
31 | - if (!StringUtils.isEmpty(configuration)) { | |
32 | - try { | |
33 | - return new SimpleTransportRateLimit(new TbRateLimits(configuration.toString()), configuration.toString()); | |
34 | - } catch (Exception e) { | |
35 | - log.warn("[{}] Failed to init rate limit with configuration: {}", type, configuration, e); | |
36 | - return ALWAYS_TRUE; | |
37 | - } | |
38 | - } else { | |
39 | - return ALWAYS_TRUE; | |
35 | + public void onSuccess(TbQueueMsgMetadata metadata) { | |
36 | + if (tbQueueCallbackCount.decrementAndGet() <= 0) { | |
37 | + callback.onSuccess(metadata); | |
40 | 38 | } |
41 | 39 | } |
42 | 40 | |
43 | 41 | @Override |
44 | - public TransportRateLimit createDefault(TransportRateLimitType type) { | |
45 | - return ALWAYS_TRUE; | |
42 | + public void onFailure(Throwable t) { | |
43 | + callback.onFailure(new RuleEngineException(t.getMessage())); | |
46 | 44 | } |
47 | 45 | } | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
20 | 20 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
21 | 21 | import org.springframework.context.annotation.DependsOn; |
22 | 22 | import org.springframework.context.event.EventListener; |
23 | +import org.springframework.core.annotation.Order; | |
23 | 24 | import org.springframework.stereotype.Service; |
24 | 25 | |
25 | 26 | import java.util.Collections; |
... | ... | @@ -40,6 +41,7 @@ public class DummyDiscoveryService implements DiscoveryService { |
40 | 41 | } |
41 | 42 | |
42 | 43 | @EventListener(ApplicationReadyEvent.class) |
44 | + @Order(value = 1) | |
43 | 45 | public void onApplicationEvent(ApplicationReadyEvent event) { |
44 | 46 | partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), Collections.emptyList()); |
45 | 47 | } | ... | ... |
... | ... | @@ -34,6 +34,7 @@ import org.springframework.beans.factory.annotation.Value; |
34 | 34 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
35 | 35 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
36 | 36 | import org.springframework.context.event.EventListener; |
37 | +import org.springframework.core.annotation.Order; | |
37 | 38 | import org.springframework.stereotype.Service; |
38 | 39 | import org.springframework.util.Assert; |
39 | 40 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
... | ... | @@ -112,6 +113,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi |
112 | 113 | } |
113 | 114 | |
114 | 115 | @EventListener(ApplicationReadyEvent.class) |
116 | + @Order(value = 1) | |
115 | 117 | public void onApplicationEvent(ApplicationReadyEvent event) { |
116 | 118 | if (stopped) { |
117 | 119 | log.debug("Ignoring application ready event. Service is stopped."); | ... | ... |
... | ... | @@ -22,7 +22,7 @@ import org.springframework.stereotype.Component; |
22 | 22 | import org.thingsboard.server.common.msg.queue.ServiceType; |
23 | 23 | import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; |
24 | 24 | import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; |
25 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
25 | +import org.thingsboard.server.gen.transport.TransportProtos.*; | |
26 | 26 | import org.thingsboard.server.queue.TbQueueAdmin; |
27 | 27 | import org.thingsboard.server.queue.TbQueueConsumer; |
28 | 28 | import org.thingsboard.server.queue.TbQueueProducer; |
... | ... | @@ -91,64 +91,64 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng |
91 | 91 | } |
92 | 92 | |
93 | 93 | @Override |
94 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToTransportMsg>> createTransportNotificationsMsgProducer() { | |
94 | + public TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> createTransportNotificationsMsgProducer() { | |
95 | 95 | return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, transportNotificationSettings.getNotificationsTopic()); |
96 | 96 | } |
97 | 97 | |
98 | 98 | @Override |
99 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> createRuleEngineMsgProducer() { | |
99 | + public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> createRuleEngineMsgProducer() { | |
100 | 100 | return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic()); |
101 | 101 | } |
102 | 102 | |
103 | 103 | @Override |
104 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> createRuleEngineNotificationsMsgProducer() { | |
104 | + public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> createRuleEngineNotificationsMsgProducer() { | |
105 | 105 | return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic()); |
106 | 106 | } |
107 | 107 | |
108 | 108 | @Override |
109 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> createTbCoreMsgProducer() { | |
109 | + public TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> createTbCoreMsgProducer() { | |
110 | 110 | return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); |
111 | 111 | } |
112 | 112 | |
113 | 113 | @Override |
114 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> createTbCoreNotificationsMsgProducer() { | |
114 | + public TbQueueProducer<TbProtoQueueMsg<ToCoreNotificationMsg>> createTbCoreNotificationsMsgProducer() { | |
115 | 115 | return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); |
116 | 116 | } |
117 | 117 | |
118 | 118 | @Override |
119 | - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> createToRuleEngineMsgConsumer(TbRuleEngineQueueConfiguration configuration) { | |
119 | + public TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> createToRuleEngineMsgConsumer(TbRuleEngineQueueConfiguration configuration) { | |
120 | 120 | return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic(), |
121 | - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
121 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
122 | 122 | } |
123 | 123 | |
124 | 124 | @Override |
125 | - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> createToRuleEngineNotificationsMsgConsumer() { | |
125 | + public TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> createToRuleEngineNotificationsMsgConsumer() { | |
126 | 126 | return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, |
127 | 127 | partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), |
128 | - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
128 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
129 | 129 | } |
130 | 130 | |
131 | 131 | @Override |
132 | - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> createToCoreMsgConsumer() { | |
132 | + public TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> createToCoreMsgConsumer() { | |
133 | 133 | return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic(), |
134 | - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
134 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
135 | 135 | } |
136 | 136 | |
137 | 137 | @Override |
138 | - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> createToCoreNotificationsMsgConsumer() { | |
138 | + public TbQueueConsumer<TbProtoQueueMsg<ToCoreNotificationMsg>> createToCoreNotificationsMsgConsumer() { | |
139 | 139 | return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, |
140 | 140 | partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), |
141 | - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
141 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
142 | 142 | } |
143 | 143 | |
144 | 144 | @Override |
145 | - public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg>> createTransportApiRequestConsumer() { | |
145 | + public TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> createTransportApiRequestConsumer() { | |
146 | 146 | return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getRequestsTopic(), |
147 | - msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
147 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
148 | 148 | } |
149 | 149 | |
150 | 150 | @Override |
151 | - public TbQueueProducer<TbProtoQueueMsg<TransportProtos.TransportApiResponseMsg>> createTransportApiResponseProducer() { | |
151 | + public TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> createTransportApiResponseProducer() { | |
152 | 152 | return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getResponsesTopic()); |
153 | 153 | } |
154 | 154 | |
... | ... | @@ -175,6 +175,17 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng |
175 | 175 | return builder.build(); |
176 | 176 | } |
177 | 177 | |
178 | + @Override | |
179 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
180 | + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); | |
181 | + } | |
182 | + | |
183 | + @Override | |
184 | + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
185 | + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getUsageStatsTopic(), | |
186 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
187 | + } | |
188 | + | |
178 | 189 | @PreDestroy |
179 | 190 | private void destroy() { |
180 | 191 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -21,6 +21,7 @@ import org.springframework.context.annotation.Bean; |
21 | 21 | import org.springframework.stereotype.Component; |
22 | 22 | import org.thingsboard.server.common.msg.queue.ServiceType; |
23 | 23 | import org.thingsboard.server.gen.js.JsInvokeProtos; |
24 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
24 | 25 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
25 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; |
26 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
... | ... | @@ -163,6 +164,17 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { |
163 | 164 | return builder.build(); |
164 | 165 | } |
165 | 166 | |
167 | + @Override | |
168 | + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
169 | + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); | |
170 | + } | |
171 | + | |
172 | + @Override | |
173 | + public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
174 | + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getUsageStatsTopic(), | |
175 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
176 | + } | |
177 | + | |
166 | 178 | @PreDestroy |
167 | 179 | private void destroy() { |
168 | 180 | if (coreAdmin != null) { | ... | ... |
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java
... | ... | @@ -143,6 +143,11 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory |
143 | 143 | return builder.build(); |
144 | 144 | } |
145 | 145 | |
146 | + @Override | |
147 | + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
148 | + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); | |
149 | + } | |
150 | + | |
146 | 151 | @PreDestroy |
147 | 152 | private void destroy() { |
148 | 153 | if (coreAdmin != null) { |
... | ... | @@ -158,4 +163,5 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory |
158 | 163 | notificationAdmin.destroy(); |
159 | 164 | } |
160 | 165 | } |
166 | + | |
161 | 167 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.provider; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
20 | 20 | import org.springframework.stereotype.Component; |
21 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
21 | 22 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
22 | 23 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
23 | 24 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
... | ... | @@ -109,6 +110,11 @@ public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { |
109 | 110 | msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); |
110 | 111 | } |
111 | 112 | |
113 | + @Override | |
114 | + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
115 | + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); | |
116 | + } | |
117 | + | |
112 | 118 | @PreDestroy |
113 | 119 | private void destroy() { |
114 | 120 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -125,8 +125,20 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE |
125 | 125 | return null; |
126 | 126 | } |
127 | 127 | |
128 | + @Override | |
129 | + public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
130 | + return new InMemoryTbQueueConsumer<>(coreSettings.getUsageStatsTopic()); | |
131 | + } | |
132 | + | |
133 | + @Override | |
134 | + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
135 | + return new InMemoryTbQueueProducer<>(coreSettings.getUsageStatsTopic()); | |
136 | + } | |
137 | + | |
128 | 138 | @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") |
129 | 139 | private void printInMemoryStats() { |
130 | 140 | storage.printStats(); |
131 | 141 | } |
142 | + | |
143 | + | |
132 | 144 | } | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.provider; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
20 | 20 | import org.springframework.stereotype.Component; |
21 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
21 | 22 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
22 | 23 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
23 | 24 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
... | ... | @@ -96,4 +97,10 @@ public class InMemoryTbTransportQueueFactory implements TbTransportQueueFactory |
96 | 97 | public TbQueueConsumer<TbProtoQueueMsg<ToTransportMsg>> createTransportNotificationsConsumer() { |
97 | 98 | return new InMemoryTbQueueConsumer<>(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()); |
98 | 99 | } |
100 | + | |
101 | + @Override | |
102 | + public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
103 | + return new InMemoryTbQueueProducer<>(coreSettings.getUsageStatsTopic()); | |
104 | + } | |
105 | + | |
99 | 106 | } | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMs |
26 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
27 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
29 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
29 | 30 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
30 | 31 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
31 | 32 | import org.thingsboard.server.queue.TbQueueAdmin; |
... | ... | @@ -249,6 +250,28 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi |
249 | 250 | return builder.build(); |
250 | 251 | } |
251 | 252 | |
253 | + @Override | |
254 | + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
255 | + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder(); | |
256 | + consumerBuilder.settings(kafkaSettings); | |
257 | + consumerBuilder.topic(coreSettings.getUsageStatsTopic()); | |
258 | + consumerBuilder.clientId("monolith-us-consumer-" + serviceInfoProvider.getServiceId()); | |
259 | + consumerBuilder.groupId("monolith-us-consumer-" + serviceInfoProvider.getServiceId()); | |
260 | + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
261 | + consumerBuilder.admin(coreAdmin); | |
262 | + return consumerBuilder.build(); | |
263 | + } | |
264 | + | |
265 | + @Override | |
266 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
267 | + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); | |
268 | + requestBuilder.settings(kafkaSettings); | |
269 | + requestBuilder.clientId("monolith-us-producer-" + serviceInfoProvider.getServiceId()); | |
270 | + requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); | |
271 | + requestBuilder.admin(coreAdmin); | |
272 | + return requestBuilder.build(); | |
273 | + } | |
274 | + | |
252 | 275 | @PreDestroy |
253 | 276 | private void destroy() { |
254 | 277 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMs |
26 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
27 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
29 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
29 | 30 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
30 | 31 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
31 | 32 | import org.thingsboard.server.queue.TbQueueAdmin; |
... | ... | @@ -219,6 +220,28 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { |
219 | 220 | return builder.build(); |
220 | 221 | } |
221 | 222 | |
223 | + @Override | |
224 | + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
225 | + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder(); | |
226 | + consumerBuilder.settings(kafkaSettings); | |
227 | + consumerBuilder.topic(coreSettings.getUsageStatsTopic()); | |
228 | + consumerBuilder.clientId("tb-core-us-consumer-" + serviceInfoProvider.getServiceId()); | |
229 | + consumerBuilder.groupId("tb-core-us-consumer-" + serviceInfoProvider.getServiceId()); | |
230 | + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
231 | + consumerBuilder.admin(coreAdmin); | |
232 | + return consumerBuilder.build(); | |
233 | + } | |
234 | + | |
235 | + @Override | |
236 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
237 | + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); | |
238 | + requestBuilder.settings(kafkaSettings); | |
239 | + requestBuilder.clientId("tb-core-us-producer-" + serviceInfoProvider.getServiceId()); | |
240 | + requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); | |
241 | + requestBuilder.admin(coreAdmin); | |
242 | + return requestBuilder.build(); | |
243 | + } | |
244 | + | |
222 | 245 | @PreDestroy |
223 | 246 | private void destroy() { |
224 | 247 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMs |
26 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
27 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
29 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
29 | 30 | import org.thingsboard.server.queue.TbQueueAdmin; |
30 | 31 | import org.thingsboard.server.queue.TbQueueConsumer; |
31 | 32 | import org.thingsboard.server.queue.TbQueueProducer; |
... | ... | @@ -192,6 +193,16 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { |
192 | 193 | return builder.build(); |
193 | 194 | } |
194 | 195 | |
196 | + @Override | |
197 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
198 | + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); | |
199 | + requestBuilder.settings(kafkaSettings); | |
200 | + requestBuilder.clientId("tb-rule-engine-us-producer-" + serviceInfoProvider.getServiceId()); | |
201 | + requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); | |
202 | + requestBuilder.admin(coreAdmin); | |
203 | + return requestBuilder.build(); | |
204 | + } | |
205 | + | |
195 | 206 | @PreDestroy |
196 | 207 | private void destroy() { |
197 | 208 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -21,6 +21,7 @@ import org.springframework.stereotype.Component; |
21 | 21 | import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; |
22 | 22 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
23 | 23 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
24 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
24 | 25 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
25 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
26 | 27 | import org.thingsboard.server.queue.TbQueueAdmin; |
... | ... | @@ -138,6 +139,16 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { |
138 | 139 | return responseBuilder.build(); |
139 | 140 | } |
140 | 141 | |
142 | + @Override | |
143 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
144 | + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToUsageStatsServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder(); | |
145 | + requestBuilder.settings(kafkaSettings); | |
146 | + requestBuilder.clientId("transport-node-us-producer-" + serviceInfoProvider.getServiceId()); | |
147 | + requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); | |
148 | + requestBuilder.admin(coreAdmin); | |
149 | + return requestBuilder.build(); | |
150 | + } | |
151 | + | |
141 | 152 | @PreDestroy |
142 | 153 | private void destroy() { |
143 | 154 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMs |
27 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
29 | 29 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
30 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
30 | 31 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
31 | 32 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
32 | 33 | import org.thingsboard.server.queue.TbQueueAdmin; |
... | ... | @@ -183,6 +184,17 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng |
183 | 184 | return builder.build(); |
184 | 185 | } |
185 | 186 | |
187 | + @Override | |
188 | + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
189 | + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), | |
190 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
191 | + } | |
192 | + | |
193 | + @Override | |
194 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
195 | + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); | |
196 | + } | |
197 | + | |
186 | 198 | @PreDestroy |
187 | 199 | private void destroy() { |
188 | 200 | if (coreAdmin != null) { | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMs |
26 | 26 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; |
27 | 27 | import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
29 | +import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; | |
29 | 30 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
30 | 31 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
31 | 32 | import org.thingsboard.server.queue.TbQueueAdmin; |
... | ... | @@ -157,6 +158,17 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { |
157 | 158 | return builder.build(); |
158 | 159 | } |
159 | 160 | |
161 | + @Override | |
162 | + public TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgConsumer() { | |
163 | + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), | |
164 | + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); | |
165 | + } | |
166 | + | |
167 | + @Override | |
168 | + public TbQueueProducer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> createToUsageStatsServiceMsgProducer() { | |
169 | + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); | |
170 | + } | |
171 | + | |
160 | 172 | @PreDestroy |
161 | 173 | private void destroy() { |
162 | 174 | if (coreAdmin != null) { | ... | ... |