Commit bee943baab1ac3ec038553019240fa1e26f88047

Authored by Andrew Shvayka
1 parent 9495c29d

Initial implementation of Asset, Relation and Alarm services

Showing 66 changed files with 3803 additions and 161 deletions

Too many changes to show.

To preserve performance only 66 of 185 files are displayed.

... ... @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
37 37 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 38 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
39 39 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
  40 +import org.thingsboard.server.dao.asset.AssetService;
40 41 import org.thingsboard.server.dao.attributes.AttributesService;
41 42 import org.thingsboard.server.dao.customer.CustomerService;
42 43 import org.thingsboard.server.dao.device.DeviceService;
... ... @@ -81,6 +82,9 @@ public class ActorSystemContext {
81 82 @Getter private DeviceService deviceService;
82 83
83 84 @Autowired
  85 + @Getter private AssetService assetService;
  86 +
  87 + @Autowired
84 88 @Getter private TenantService tenantService;
85 89
86 90 @Autowired
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.actors.plugin;
17 17
18 18 import java.io.IOException;
19 19 import java.util.*;
20   -import java.util.concurrent.ExecutionException;
21 20 import java.util.concurrent.Executor;
22 21 import java.util.concurrent.Executors;
23 22 import java.util.stream.Collectors;
... ... @@ -30,15 +29,19 @@ import com.google.common.util.concurrent.FutureCallback;
30 29 import com.google.common.util.concurrent.Futures;
31 30 import com.google.common.util.concurrent.ListenableFuture;
32 31 import lombok.extern.slf4j.Slf4j;
33   -import org.thingsboard.server.common.data.DataConstants;
  32 +import org.thingsboard.server.common.data.Customer;
34 33 import org.thingsboard.server.common.data.Device;
  34 +import org.thingsboard.server.common.data.EntityType;
  35 +import org.thingsboard.server.common.data.Tenant;
  36 +import org.thingsboard.server.common.data.asset.Asset;
35 37 import org.thingsboard.server.common.data.id.*;
36 38 import org.thingsboard.server.common.data.kv.AttributeKey;
37 39 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
38 40 import org.thingsboard.server.common.data.kv.TsKvEntry;
39 41 import org.thingsboard.server.common.data.kv.TsKvQuery;
40   -import org.thingsboard.server.common.data.page.TextPageData;
41 42 import org.thingsboard.server.common.data.page.TextPageLink;
  43 +import org.thingsboard.server.common.data.plugin.PluginMetaData;
  44 +import org.thingsboard.server.common.data.rule.RuleMetaData;
42 45 import org.thingsboard.server.common.msg.cluster.ServerAddress;
43 46 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
44 47 import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
... ... @@ -53,7 +56,6 @@ import org.thingsboard.server.extensions.api.plugins.ws.PluginWebsocketSessionRe
53 56 import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
54 57
55 58 import akka.actor.ActorRef;
56   -import org.w3c.dom.Attr;
57 59
58 60 import javax.annotation.Nullable;
59 61
... ... @@ -91,103 +93,107 @@ public final class PluginProcessingContext implements PluginContext {
91 93 }
92 94
93 95 @Override
94   - public void saveAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List<AttributeKvEntry> attributes, final PluginCallback<Void> callback) {
95   - validate(deviceId, new ValidationCallback(callback, ctx -> {
96   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.attributesService.save(deviceId, scope, attributes);
  96 + public void saveAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<AttributeKvEntry> attributes, final PluginCallback<Void> callback) {
  97 + validate(entityId, new ValidationCallback(callback, ctx -> {
  98 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.attributesService.save(entityId, scope, attributes);
97 99 Futures.addCallback(rsListFuture, getListCallback(callback, v -> {
98   - onDeviceAttributesChanged(tenantId, deviceId, scope, attributes);
  100 + if (entityId.getEntityType() == EntityType.DEVICE) {
  101 + onDeviceAttributesChanged(tenantId, new DeviceId(entityId.getId()), scope, attributes);
  102 + }
99 103 return null;
100 104 }), executor);
101 105 }));
102 106 }
103 107
104 108 @Override
105   - public void removeAttributes(final TenantId tenantId, final DeviceId deviceId, final String scope, final List<String> keys, final PluginCallback<Void> callback) {
106   - validate(deviceId, new ValidationCallback(callback, ctx -> {
107   - ListenableFuture<List<ResultSet>> future = pluginCtx.attributesService.removeAll(deviceId, scope, keys);
  109 + public void removeAttributes(final TenantId tenantId, final EntityId entityId, final String scope, final List<String> keys, final PluginCallback<Void> callback) {
  110 + validate(entityId, new ValidationCallback(callback, ctx -> {
  111 + ListenableFuture<List<ResultSet>> future = pluginCtx.attributesService.removeAll(entityId, scope, keys);
108 112 Futures.addCallback(future, getCallback(callback, v -> null), executor);
109   - onDeviceAttributesDeleted(tenantId, deviceId, keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
  113 + if (entityId.getEntityType() == EntityType.DEVICE) {
  114 + onDeviceAttributesDeleted(tenantId, new DeviceId(entityId.getId()), keys.stream().map(key -> new AttributeKey(scope, key)).collect(Collectors.toSet()));
  115 + }
110 116 }));
111 117 }
112 118
113 119 @Override
114   - public void loadAttribute(DeviceId deviceId, String attributeType, String attributeKey, final PluginCallback<Optional<AttributeKvEntry>> callback) {
115   - validate(deviceId, new ValidationCallback(callback, ctx -> {
116   - ListenableFuture<Optional<AttributeKvEntry>> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKey);
  120 + public void loadAttribute(EntityId entityId, String attributeType, String attributeKey, final PluginCallback<Optional<AttributeKvEntry>> callback) {
  121 + validate(entityId, new ValidationCallback(callback, ctx -> {
  122 + ListenableFuture<Optional<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKey);
117 123 Futures.addCallback(future, getCallback(callback, v -> v), executor);
118 124 }));
119 125 }
120 126
121 127 @Override
122   - public void loadAttributes(DeviceId deviceId, String attributeType, Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
123   - validate(deviceId, new ValidationCallback(callback, ctx -> {
124   - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys);
  128 + public void loadAttributes(EntityId entityId, String attributeType, Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
  129 + validate(entityId, new ValidationCallback(callback, ctx -> {
  130 + ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.find(entityId, attributeType, attributeKeys);
125 131 Futures.addCallback(future, getCallback(callback, v -> v), executor);
126 132 }));
127 133 }
128 134
129 135 @Override
130   - public void loadAttributes(DeviceId deviceId, String attributeType, PluginCallback<List<AttributeKvEntry>> callback) {
131   - validate(deviceId, new ValidationCallback(callback, ctx -> {
132   - ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.findAll(deviceId, attributeType);
  136 + public void loadAttributes(EntityId entityId, String attributeType, PluginCallback<List<AttributeKvEntry>> callback) {
  137 + validate(entityId, new ValidationCallback(callback, ctx -> {
  138 + ListenableFuture<List<AttributeKvEntry>> future = pluginCtx.attributesService.findAll(entityId, attributeType);
133 139 Futures.addCallback(future, getCallback(callback, v -> v), executor);
134 140 }));
135 141 }
136 142
137 143 @Override
138   - public void loadAttributes(final DeviceId deviceId, final Collection<String> attributeTypes, final PluginCallback<List<AttributeKvEntry>> callback) {
139   - validate(deviceId, new ValidationCallback(callback, ctx -> {
  144 + public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final PluginCallback<List<AttributeKvEntry>> callback) {
  145 + validate(entityId, new ValidationCallback(callback, ctx -> {
140 146 List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
141   - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(deviceId, attributeType)));
  147 + attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.findAll(entityId, attributeType)));
142 148 convertFuturesAndAddCallback(callback, futures);
143 149 }));
144 150 }
145 151
146 152 @Override
147   - public void loadAttributes(final DeviceId deviceId, final Collection<String> attributeTypes, final Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
148   - validate(deviceId, new ValidationCallback(callback, ctx -> {
  153 + public void loadAttributes(final EntityId entityId, final Collection<String> attributeTypes, final Collection<String> attributeKeys, final PluginCallback<List<AttributeKvEntry>> callback) {
  154 + validate(entityId, new ValidationCallback(callback, ctx -> {
149 155 List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
150   - attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(deviceId, attributeType, attributeKeys)));
  156 + attributeTypes.forEach(attributeType -> futures.add(pluginCtx.attributesService.find(entityId, attributeType, attributeKeys)));
151 157 convertFuturesAndAddCallback(callback, futures);
152 158 }));
153 159 }
154 160
155 161 @Override
156   - public void saveTsData(final DeviceId deviceId, final TsKvEntry entry, final PluginCallback<Void> callback) {
157   - validate(deviceId, new ValidationCallback(callback, ctx -> {
158   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entry);
  162 + public void saveTsData(final EntityId entityId, final TsKvEntry entry, final PluginCallback<Void> callback) {
  163 + validate(entityId, new ValidationCallback(callback, ctx -> {
  164 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(entityId, entry);
159 165 Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
160 166 }));
161 167 }
162 168
163 169 @Override
164   - public void saveTsData(final DeviceId deviceId, final List<TsKvEntry> entries, final PluginCallback<Void> callback) {
165   - validate(deviceId, new ValidationCallback(callback, ctx -> {
166   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(DataConstants.DEVICE, deviceId, entries);
  170 + public void saveTsData(final EntityId entityId, final List<TsKvEntry> entries, final PluginCallback<Void> callback) {
  171 + validate(entityId, new ValidationCallback(callback, ctx -> {
  172 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.save(entityId, entries);
167 173 Futures.addCallback(rsListFuture, getListCallback(callback, v -> null), executor);
168 174 }));
169 175 }
170 176
171 177 @Override
172   - public void loadTimeseries(final DeviceId deviceId, final List<TsKvQuery> queries, final PluginCallback<List<TsKvEntry>> callback) {
173   - validate(deviceId, new ValidationCallback(callback, ctx -> {
174   - ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(DataConstants.DEVICE, deviceId, queries);
  178 + public void loadTimeseries(final EntityId entityId, final List<TsKvQuery> queries, final PluginCallback<List<TsKvEntry>> callback) {
  179 + validate(entityId, new ValidationCallback(callback, ctx -> {
  180 + ListenableFuture<List<TsKvEntry>> future = pluginCtx.tsService.findAll(entityId, queries);
175 181 Futures.addCallback(future, getCallback(callback, v -> v), executor);
176 182 }));
177 183 }
178 184
179 185 @Override
180   - public void loadLatestTimeseries(final DeviceId deviceId, final PluginCallback<List<TsKvEntry>> callback) {
181   - validate(deviceId, new ValidationCallback(callback, ctx -> {
182   - ResultSetFuture future = pluginCtx.tsService.findAllLatest(DataConstants.DEVICE, deviceId);
  186 + public void loadLatestTimeseries(final EntityId entityId, final PluginCallback<List<TsKvEntry>> callback) {
  187 + validate(entityId, new ValidationCallback(callback, ctx -> {
  188 + ResultSetFuture future = pluginCtx.tsService.findAllLatest(entityId);
183 189 Futures.addCallback(future, getCallback(callback, pluginCtx.tsService::convertResultSetToTsKvEntryList), executor);
184 190 }));
185 191 }
186 192
187 193 @Override
188   - public void loadLatestTimeseries(final DeviceId deviceId, final Collection<String> keys, final PluginCallback<List<TsKvEntry>> callback) {
189   - validate(deviceId, new ValidationCallback(callback, ctx -> {
190   - ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.findLatest(DataConstants.DEVICE, deviceId, keys);
  194 + public void loadLatestTimeseries(final EntityId entityId, final Collection<String> keys, final PluginCallback<List<TsKvEntry>> callback) {
  195 + validate(entityId, new ValidationCallback(callback, ctx -> {
  196 + ListenableFuture<List<ResultSet>> rsListFuture = pluginCtx.tsService.findLatest(entityId, keys);
191 197 Futures.addCallback(rsListFuture, getListCallback(callback, rsList ->
192 198 {
193 199 List<TsKvEntry> result = new ArrayList<>();
... ... @@ -270,24 +276,101 @@ public final class PluginProcessingContext implements PluginContext {
270 276 validate(deviceId, new ValidationCallback(callback, ctx -> callback.onSuccess(ctx, null)));
271 277 }
272 278
273   - private void validate(DeviceId deviceId, ValidationCallback callback) {
  279 + private void validate(EntityId entityId, ValidationCallback callback) {
274 280 if (securityCtx.isPresent()) {
275 281 final PluginApiCallSecurityContext ctx = securityCtx.get();
276   - if (ctx.isTenantAdmin() || ctx.isCustomerUser()) {
277   - ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(deviceId);
278   - Futures.addCallback(deviceFuture, getCallback(callback, device -> {
279   - if (device == null) {
280   - return Boolean.FALSE;
281   - } else {
282   - if (!device.getTenantId().equals(ctx.getTenantId())) {
283   - return Boolean.FALSE;
284   - } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
285   - return Boolean.FALSE;
  282 + if (ctx.isTenantAdmin() || ctx.isCustomerUser() || ctx.isSystemAdmin()) {
  283 + switch (entityId.getEntityType()) {
  284 + case DEVICE:
  285 + if (ctx.isSystemAdmin()) {
  286 + callback.onSuccess(this, Boolean.FALSE);
286 287 } else {
287   - return Boolean.TRUE;
  288 + ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
  289 + Futures.addCallback(deviceFuture, getCallback(callback, device -> {
  290 + if (device == null) {
  291 + return Boolean.FALSE;
  292 + } else {
  293 + if (!device.getTenantId().equals(ctx.getTenantId())) {
  294 + return Boolean.FALSE;
  295 + } else if (ctx.isCustomerUser() && !device.getCustomerId().equals(ctx.getCustomerId())) {
  296 + return Boolean.FALSE;
  297 + } else {
  298 + return Boolean.TRUE;
  299 + }
  300 + }
  301 + }));
288 302 }
289   - }
290   - }));
  303 + return;
  304 + case ASSET:
  305 + if (ctx.isSystemAdmin()) {
  306 + callback.onSuccess(this, Boolean.FALSE);
  307 + } else {
  308 + ListenableFuture<Asset> assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
  309 + Futures.addCallback(assetFuture, getCallback(callback, asset -> {
  310 + if (asset == null) {
  311 + return Boolean.FALSE;
  312 + } else {
  313 + if (!asset.getTenantId().equals(ctx.getTenantId())) {
  314 + return Boolean.FALSE;
  315 + } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
  316 + return Boolean.FALSE;
  317 + } else {
  318 + return Boolean.TRUE;
  319 + }
  320 + }
  321 + }));
  322 + }
  323 + return;
  324 + case RULE:
  325 + if (ctx.isCustomerUser()) {
  326 + callback.onSuccess(this, Boolean.FALSE);
  327 + } else {
  328 + ListenableFuture<RuleMetaData> ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
  329 + Futures.addCallback(ruleFuture, getCallback(callback, rule -> rule != null && rule.getTenantId().equals(ctx.getTenantId())));
  330 + }
  331 + return;
  332 + case PLUGIN:
  333 + if (ctx.isCustomerUser()) {
  334 + callback.onSuccess(this, Boolean.FALSE);
  335 + } else {
  336 + ListenableFuture<PluginMetaData> pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
  337 + Futures.addCallback(pluginFuture, getCallback(callback, plugin -> plugin != null && plugin.getTenantId().equals(ctx.getTenantId())));
  338 + }
  339 + return;
  340 + case CUSTOMER:
  341 + if (ctx.isSystemAdmin()) {
  342 + callback.onSuccess(this, Boolean.FALSE);
  343 + } else {
  344 + ListenableFuture<Customer> customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
  345 + Futures.addCallback(customerFuture, getCallback(callback, customer -> {
  346 + if (customer == null) {
  347 + return Boolean.FALSE;
  348 + } else {
  349 + if (!customer.getTenantId().equals(ctx.getTenantId())) {
  350 + return Boolean.FALSE;
  351 + } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
  352 + return Boolean.FALSE;
  353 + } else {
  354 + return Boolean.TRUE;
  355 + }
  356 + }
  357 + }));
  358 + }
  359 + return;
  360 + case TENANT:
  361 + if (ctx.isCustomerUser()) {
  362 + callback.onSuccess(this, Boolean.FALSE);
  363 + } else if (ctx.isSystemAdmin()) {
  364 + callback.onSuccess(this, Boolean.TRUE);
  365 + } else {
  366 + ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
  367 + Futures.addCallback(tenantFuture, getCallback(callback, tenant -> tenant != null && tenant.getId().equals(ctx.getTenantId())));
  368 + }
  369 + return;
  370 + default:
  371 + //TODO: add support of other entities
  372 + throw new IllegalStateException("Not Implemented!");
  373 + }
291 374 } else {
292 375 callback.onSuccess(this, Boolean.FALSE);
293 376 }
... ... @@ -297,8 +380,8 @@ public final class PluginProcessingContext implements PluginContext {
297 380 }
298 381
299 382 @Override
300   - public Optional<ServerAddress> resolve(DeviceId deviceId) {
301   - return pluginCtx.routingService.resolve(deviceId);
  383 + public Optional<ServerAddress> resolve(EntityId entityId) {
  384 + return pluginCtx.routingService.resolveById(entityId);
302 385 }
303 386
304 387 @Override
... ...
... ... @@ -25,8 +25,13 @@ import org.thingsboard.server.common.data.id.TenantId;
25 25 import org.thingsboard.server.common.msg.cluster.ServerAddress;
26 26 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
27 27 import org.thingsboard.server.common.data.id.PluginId;
  28 +import org.thingsboard.server.dao.asset.AssetService;
28 29 import org.thingsboard.server.dao.attributes.AttributesService;
  30 +import org.thingsboard.server.dao.customer.CustomerService;
29 31 import org.thingsboard.server.dao.device.DeviceService;
  32 +import org.thingsboard.server.dao.plugin.PluginService;
  33 +import org.thingsboard.server.dao.rule.RuleService;
  34 +import org.thingsboard.server.dao.tenant.TenantService;
30 35 import org.thingsboard.server.dao.timeseries.TimeseriesService;
31 36 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
32 37 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
... ... @@ -46,7 +51,12 @@ public final class SharedPluginProcessingContext {
46 51 final ActorRef currentActor;
47 52 final ActorSystemContext systemContext;
48 53 final PluginWebSocketMsgEndpoint msgEndpoint;
  54 + final AssetService assetService;
49 55 final DeviceService deviceService;
  56 + final RuleService ruleService;
  57 + final PluginService pluginService;
  58 + final CustomerService customerService;
  59 + final TenantService tenantService;
50 60 final TimeseriesService tsService;
51 61 final AttributesService attributesService;
52 62 final ClusterRpcService rpcService;
... ... @@ -65,9 +75,14 @@ public final class SharedPluginProcessingContext {
65 75 this.msgEndpoint = sysContext.getWsMsgEndpoint();
66 76 this.tsService = sysContext.getTsService();
67 77 this.attributesService = sysContext.getAttributesService();
  78 + this.assetService = sysContext.getAssetService();
68 79 this.deviceService = sysContext.getDeviceService();
69 80 this.rpcService = sysContext.getRpcService();
70 81 this.routingService = sysContext.getRoutingService();
  82 + this.ruleService = sysContext.getRuleService();
  83 + this.pluginService = sysContext.getPluginService();
  84 + this.customerService = sysContext.getCustomerService();
  85 + this.tenantService = sysContext.getTenantService();
71 86 }
72 87
73 88 public PluginId getPluginId() {
... ... @@ -89,7 +104,7 @@ public final class SharedPluginProcessingContext {
89 104 }
90 105
91 106 private <T> void forward(DeviceId deviceId, T msg, BiConsumer<ServerAddress, T> rpcFunction) {
92   - Optional<ServerAddress> instance = routingService.resolve(deviceId);
  107 + Optional<ServerAddress> instance = routingService.resolveById(deviceId);
93 108 if (instance.isPresent()) {
94 109 log.trace("[{}] Forwarding msg {} to remote device actor!", pluginId, msg);
95 110 rpcFunction.accept(instance.get(), msg);
... ...
... ... @@ -38,7 +38,7 @@ public class ValidationCallback implements PluginCallback<Boolean> {
38 38 if (value) {
39 39 action.accept(ctx);
40 40 } else {
41   - onFailure(ctx, new UnauthorizedException());
  41 + onFailure(ctx, new UnauthorizedException("Permission denied."));
42 42 }
43 43 }
44 44
... ...
... ... @@ -230,7 +230,7 @@ public class DefaultActorService implements ActorService {
230 230 @Override
231 231 public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
232 232 DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
233   - Optional<ServerAddress> address = actorContext.getRoutingService().resolve(deviceId);
  233 + Optional<ServerAddress> address = actorContext.getRoutingService().resolveById(deviceId);
234 234 if (address.isPresent()) {
235 235 rpcService.tell(address.get(), msg);
236 236 } else {
... ...
... ... @@ -116,7 +116,7 @@ class ASyncMsgProcessor extends AbstractSessionActorMsgProcessor {
116 116 @Override
117 117 public void processClusterEvent(ActorContext context, ClusterEventMsg msg) {
118 118 if (pendingMap.size() > 0 || subscribedToAttributeUpdates || subscribedToRpcCommands) {
119   - Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolve(getDeviceId());
  119 + Optional<ServerAddress> newTargetServer = systemContext.getRoutingService().resolveById(getDeviceId());
120 120 if (!newTargetServer.equals(currentTargetServer)) {
121 121 firstMsg = true;
122 122 currentTargetServer = newTargetServer;
... ...
... ... @@ -81,13 +81,13 @@ abstract class AbstractSessionActorMsgProcessor extends AbstractContextAwareMsgP
81 81 }
82 82
83 83 protected Optional<ServerAddress> forwardToAppActor(ActorContext ctx, ToDeviceActorMsg toForward) {
84   - Optional<ServerAddress> address = systemContext.getRoutingService().resolve(toForward.getDeviceId());
  84 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
85 85 forwardToAppActor(ctx, toForward, address);
86 86 return address;
87 87 }
88 88
89 89 protected Optional<ServerAddress> forwardToAppActorIfAdressChanged(ActorContext ctx, ToDeviceActorMsg toForward, Optional<ServerAddress> oldAddress) {
90   - Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolve(toForward.getDeviceId());
  90 + Optional<ServerAddress> newAddress = systemContext.getRoutingService().resolveById(toForward.getDeviceId());
91 91 if (!newAddress.equals(oldAddress)) {
92 92 if (newAddress.isPresent()) {
93 93 systemContext.getRpcService().tell(newAddress.get(),
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.springframework.http.HttpStatus;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.*;
  22 +import org.thingsboard.server.common.data.Customer;
  23 +import org.thingsboard.server.common.data.asset.Asset;
  24 +import org.thingsboard.server.common.data.id.AssetId;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.page.TextPageData;
  28 +import org.thingsboard.server.common.data.page.TextPageLink;
  29 +import org.thingsboard.server.dao.asset.AssetSearchQuery;
  30 +import org.thingsboard.server.dao.exception.IncorrectParameterException;
  31 +import org.thingsboard.server.dao.model.ModelConstants;
  32 +import org.thingsboard.server.exception.ThingsboardException;
  33 +import org.thingsboard.server.service.security.model.SecurityUser;
  34 +
  35 +import java.util.ArrayList;
  36 +import java.util.List;
  37 +import java.util.stream.Collectors;
  38 +
  39 +@RestController
  40 +@RequestMapping("/api")
  41 +public class AssetController extends BaseController {
  42 +
  43 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  44 + @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET)
  45 + @ResponseBody
  46 + public Asset getAssetById(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  47 + checkParameter("assetId", strAssetId);
  48 + try {
  49 + AssetId assetId = new AssetId(toUUID(strAssetId));
  50 + return checkAssetId(assetId);
  51 + } catch (Exception e) {
  52 + throw handleException(e);
  53 + }
  54 + }
  55 +
  56 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  57 + @RequestMapping(value = "/asset", method = RequestMethod.POST)
  58 + @ResponseBody
  59 + public Asset saveAsset(@RequestBody Asset asset) throws ThingsboardException {
  60 + try {
  61 + asset.setTenantId(getCurrentUser().getTenantId());
  62 + return checkNotNull(assetService.saveAsset(asset));
  63 + } catch (Exception e) {
  64 + throw handleException(e);
  65 + }
  66 + }
  67 +
  68 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  69 + @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.DELETE)
  70 + @ResponseStatus(value = HttpStatus.OK)
  71 + public void deleteAsset(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  72 + checkParameter("assetId", strAssetId);
  73 + try {
  74 + AssetId assetId = new AssetId(toUUID(strAssetId));
  75 + checkAssetId(assetId);
  76 + assetService.deleteAsset(assetId);
  77 + } catch (Exception e) {
  78 + throw handleException(e);
  79 + }
  80 + }
  81 +
  82 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  83 + @RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST)
  84 + @ResponseBody
  85 + public Asset assignAssetToCustomer(@PathVariable("customerId") String strCustomerId,
  86 + @PathVariable("assetId") String strAssetId) throws ThingsboardException {
  87 + checkParameter("customerId", strCustomerId);
  88 + checkParameter("assetId", strAssetId);
  89 + try {
  90 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  91 + checkCustomerId(customerId);
  92 +
  93 + AssetId assetId = new AssetId(toUUID(strAssetId));
  94 + checkAssetId(assetId);
  95 +
  96 + return checkNotNull(assetService.assignAssetToCustomer(assetId, customerId));
  97 + } catch (Exception e) {
  98 + throw handleException(e);
  99 + }
  100 + }
  101 +
  102 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  103 + @RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE)
  104 + @ResponseBody
  105 + public Asset unassignAssetFromCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  106 + checkParameter("assetId", strAssetId);
  107 + try {
  108 + AssetId assetId = new AssetId(toUUID(strAssetId));
  109 + Asset asset = checkAssetId(assetId);
  110 + if (asset.getCustomerId() == null || asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  111 + throw new IncorrectParameterException("Asset isn't assigned to any customer!");
  112 + }
  113 + return checkNotNull(assetService.unassignAssetFromCustomer(assetId));
  114 + } catch (Exception e) {
  115 + throw handleException(e);
  116 + }
  117 + }
  118 +
  119 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  120 + @RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST)
  121 + @ResponseBody
  122 + public Asset assignAssetToPublicCustomer(@PathVariable("assetId") String strAssetId) throws ThingsboardException {
  123 + checkParameter("assetId", strAssetId);
  124 + try {
  125 + AssetId assetId = new AssetId(toUUID(strAssetId));
  126 + Asset asset = checkAssetId(assetId);
  127 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(asset.getTenantId());
  128 + return checkNotNull(assetService.assignAssetToCustomer(assetId, publicCustomer.getId()));
  129 + } catch (Exception e) {
  130 + throw handleException(e);
  131 + }
  132 + }
  133 +
  134 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  135 + @RequestMapping(value = "/tenant/assets", params = {"limit"}, method = RequestMethod.GET)
  136 + @ResponseBody
  137 + public TextPageData<Asset> getTenantAssets(
  138 + @RequestParam int limit,
  139 + @RequestParam(required = false) String textSearch,
  140 + @RequestParam(required = false) String idOffset,
  141 + @RequestParam(required = false) String textOffset) throws ThingsboardException {
  142 + try {
  143 + TenantId tenantId = getCurrentUser().getTenantId();
  144 + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
  145 + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
  146 + } catch (Exception e) {
  147 + throw handleException(e);
  148 + }
  149 + }
  150 +
  151 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  152 + @RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
  153 + @ResponseBody
  154 + public Asset getTenantAsset(
  155 + @RequestParam String assetName) throws ThingsboardException {
  156 + try {
  157 + TenantId tenantId = getCurrentUser().getTenantId();
  158 + return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
  159 + } catch (Exception e) {
  160 + throw handleException(e);
  161 + }
  162 + }
  163 +
  164 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  165 + @RequestMapping(value = "/customer/{customerId}/assets", params = {"limit"}, method = RequestMethod.GET)
  166 + @ResponseBody
  167 + public TextPageData<Asset> getCustomerAssets(
  168 + @PathVariable("customerId") String strCustomerId,
  169 + @RequestParam int limit,
  170 + @RequestParam(required = false) String textSearch,
  171 + @RequestParam(required = false) String idOffset,
  172 + @RequestParam(required = false) String textOffset) throws ThingsboardException {
  173 + checkParameter("customerId", strCustomerId);
  174 + try {
  175 + TenantId tenantId = getCurrentUser().getTenantId();
  176 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  177 + checkCustomerId(customerId);
  178 + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
  179 + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  180 + } catch (Exception e) {
  181 + throw handleException(e);
  182 + }
  183 + }
  184 +
  185 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  186 + @RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
  187 + @ResponseBody
  188 + public List<Asset> getAssetsByIds(
  189 + @RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException {
  190 + checkArrayParameter("assetIds", strAssetIds);
  191 + try {
  192 + SecurityUser user = getCurrentUser();
  193 + TenantId tenantId = user.getTenantId();
  194 + CustomerId customerId = user.getCustomerId();
  195 + List<AssetId> assetIds = new ArrayList<>();
  196 + for (String strAssetId : strAssetIds) {
  197 + assetIds.add(new AssetId(toUUID(strAssetId)));
  198 + }
  199 + ListenableFuture<List<Asset>> assets;
  200 + if (customerId == null || customerId.isNullUid()) {
  201 + assets = assetService.findAssetsByTenantIdAndIdsAsync(tenantId, assetIds);
  202 + } else {
  203 + assets = assetService.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, assetIds);
  204 + }
  205 + return checkNotNull(assets.get());
  206 + } catch (Exception e) {
  207 + throw handleException(e);
  208 + }
  209 + }
  210 +
  211 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  212 + @RequestMapping(value = "/assets", method = RequestMethod.POST)
  213 + @ResponseBody
  214 + public List<Asset> findByQuery(@RequestBody AssetSearchQuery query) throws ThingsboardException {
  215 + checkNotNull(query);
  216 + checkNotNull(query.getParameters());
  217 + checkNotNull(query.getAssetTypes());
  218 + checkEntityId(query.getParameters().getEntityId());
  219 + try {
  220 + List<Asset> assets = checkNotNull(assetService.findAssetsByQuery(query).get());
  221 + assets = assets.stream().filter(asset -> {
  222 + try {
  223 + checkAsset(asset);
  224 + return true;
  225 + } catch (ThingsboardException e) {
  226 + return false;
  227 + }
  228 + }).collect(Collectors.toList());
  229 + return assets;
  230 + } catch (Exception e) {
  231 + throw handleException(e);
  232 + }
  233 + }
  234 +}
... ...
... ... @@ -24,10 +24,8 @@ import org.springframework.security.core.Authentication;
24 24 import org.springframework.security.core.context.SecurityContextHolder;
25 25 import org.springframework.web.bind.annotation.ExceptionHandler;
26 26 import org.thingsboard.server.actors.service.ActorService;
27   -import org.thingsboard.server.common.data.Customer;
28   -import org.thingsboard.server.common.data.Dashboard;
29   -import org.thingsboard.server.common.data.Device;
30   -import org.thingsboard.server.common.data.User;
  27 +import org.thingsboard.server.common.data.*;
  28 +import org.thingsboard.server.common.data.asset.Asset;
31 29 import org.thingsboard.server.common.data.id.*;
32 30 import org.thingsboard.server.common.data.page.TextPageLink;
33 31 import org.thingsboard.server.common.data.page.TimePageLink;
... ... @@ -38,6 +36,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData;
38 36 import org.thingsboard.server.common.data.security.Authority;
39 37 import org.thingsboard.server.common.data.widget.WidgetType;
40 38 import org.thingsboard.server.common.data.widget.WidgetsBundle;
  39 +import org.thingsboard.server.dao.asset.AssetService;
41 40 import org.thingsboard.server.dao.customer.CustomerService;
42 41 import org.thingsboard.server.dao.dashboard.DashboardService;
43 42 import org.thingsboard.server.dao.device.DeviceCredentialsService;
... ... @@ -46,6 +45,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
46 45 import org.thingsboard.server.dao.exception.IncorrectParameterException;
47 46 import org.thingsboard.server.dao.model.ModelConstants;
48 47 import org.thingsboard.server.dao.plugin.PluginService;
  48 +import org.thingsboard.server.dao.relation.RelationService;
49 49 import org.thingsboard.server.dao.rule.RuleService;
50 50 import org.thingsboard.server.dao.user.UserService;
51 51 import org.thingsboard.server.dao.widget.WidgetTypeService;
... ... @@ -81,6 +81,9 @@ public abstract class BaseController {
81 81 protected DeviceService deviceService;
82 82
83 83 @Autowired
  84 + protected AssetService assetService;
  85 +
  86 + @Autowired
84 87 protected DeviceCredentialsService deviceCredentialsService;
85 88
86 89 @Autowired
... ... @@ -104,6 +107,9 @@ public abstract class BaseController {
104 107 @Autowired
105 108 protected ActorService actorService;
106 109
  110 + @Autowired
  111 + protected RelationService relationService;
  112 +
107 113
108 114 @ExceptionHandler(ThingsboardException.class)
109 115 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
... ... @@ -253,6 +259,43 @@ public abstract class BaseController {
253 259 }
254 260 }
255 261
  262 + protected void checkEntityId(EntityId entityId) throws ThingsboardException {
  263 + try {
  264 + checkNotNull(entityId);
  265 + validateId(entityId.getId(), "Incorrect entityId " + entityId);
  266 + switch (entityId.getEntityType()) {
  267 + case DEVICE:
  268 + checkDevice(deviceService.findDeviceById(new DeviceId(entityId.getId())));
  269 + return;
  270 + case CUSTOMER:
  271 + checkCustomerId(new CustomerId(entityId.getId()));
  272 + return;
  273 + case TENANT:
  274 + checkTenantId(new TenantId(entityId.getId()));
  275 + return;
  276 + case PLUGIN:
  277 + checkPlugin(new PluginId(entityId.getId()));
  278 + return;
  279 + case RULE:
  280 + checkRule(new RuleId(entityId.getId()));
  281 + return;
  282 + case ASSET:
  283 + checkAsset(assetService.findAssetById(new AssetId(entityId.getId())));
  284 + return;
  285 + case DASHBOARD:
  286 + checkDashboardId(new DashboardId(entityId.getId()));
  287 + return;
  288 + case USER:
  289 + checkUserId(new UserId(entityId.getId()));
  290 + return;
  291 + default:
  292 + throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
  293 + }
  294 + } catch (Exception e) {
  295 + throw handleException(e, false);
  296 + }
  297 + }
  298 +
256 299 Device checkDeviceId(DeviceId deviceId) throws ThingsboardException {
257 300 try {
258 301 validateId(deviceId, "Incorrect deviceId " + deviceId);
... ... @@ -272,6 +315,25 @@ public abstract class BaseController {
272 315 }
273 316 }
274 317
  318 + Asset checkAssetId(AssetId assetId) throws ThingsboardException {
  319 + try {
  320 + validateId(assetId, "Incorrect assetId " + assetId);
  321 + Asset asset = assetService.findAssetById(assetId);
  322 + checkAsset(asset);
  323 + return asset;
  324 + } catch (Exception e) {
  325 + throw handleException(e, false);
  326 + }
  327 + }
  328 +
  329 + protected void checkAsset(Asset asset) throws ThingsboardException {
  330 + checkNotNull(asset);
  331 + checkTenantId(asset.getTenantId());
  332 + if (asset.getCustomerId() != null && !asset.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  333 + checkCustomerId(asset.getCustomerId());
  334 + }
  335 + }
  336 +
275 337 WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
276 338 try {
277 339 validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);
... ... @@ -387,6 +449,16 @@ public abstract class BaseController {
387 449 return plugin;
388 450 }
389 451
  452 + protected PluginMetaData checkPlugin(PluginId pluginId) throws ThingsboardException {
  453 + checkNotNull(pluginId);
  454 + return checkPlugin(pluginService.findPluginById(pluginId));
  455 + }
  456 +
  457 + protected RuleMetaData checkRule(RuleId ruleId) throws ThingsboardException {
  458 + checkNotNull(ruleId);
  459 + return checkRule(ruleService.findRuleById(ruleId));
  460 + }
  461 +
390 462 protected RuleMetaData checkRule(RuleMetaData rule) throws ThingsboardException {
391 463 checkNotNull(rule);
392 464 SecurityUser authUser = getCurrentUser();
... ... @@ -412,7 +484,8 @@ public abstract class BaseController {
412 484 if (request.getHeader("x-forwarded-port") != null) {
413 485 try {
414 486 serverPort = request.getIntHeader("x-forwarded-port");
415   - } catch (NumberFormatException e) {}
  487 + } catch (NumberFormatException e) {
  488 + }
416 489 }
417 490
418 491 String baseUrl = String.format("%s://%s:%d",
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import org.springframework.http.HttpStatus;
  19 +import org.springframework.security.access.prepost.PreAuthorize;
  20 +import org.springframework.web.bind.annotation.*;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  23 +import org.thingsboard.server.common.data.relation.EntityRelation;
  24 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  25 +import org.thingsboard.server.exception.ThingsboardErrorCode;
  26 +import org.thingsboard.server.exception.ThingsboardException;
  27 +
  28 +import java.util.List;
  29 +
  30 +
  31 +@RestController
  32 +@RequestMapping("/api")
  33 +public class EntityRelationController extends BaseController {
  34 +
  35 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  36 + @RequestMapping(value = "/relation", method = RequestMethod.POST)
  37 + @ResponseStatus(value = HttpStatus.OK)
  38 + public void saveRelation(@RequestBody EntityRelation relation) throws ThingsboardException {
  39 + try {
  40 + checkNotNull(relation);
  41 + checkEntityId(relation.getFrom());
  42 + checkEntityId(relation.getTo());
  43 + relationService.saveRelation(relation).get();
  44 + } catch (Exception e) {
  45 + throw handleException(e);
  46 + }
  47 + }
  48 +
  49 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  50 + @RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {"fromId", "fromType", "relationType", "toId", "toType"})
  51 + @ResponseStatus(value = HttpStatus.OK)
  52 + public void deleteRelation(@RequestParam("fromId") String strFromId,
  53 + @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
  54 + @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  55 + checkParameter("fromId", strFromId);
  56 + checkParameter("fromType", strFromType);
  57 + checkParameter("relationType", strRelationType);
  58 + checkParameter("toId", strToId);
  59 + checkParameter("toType", strToType);
  60 + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  61 + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  62 + checkEntityId(fromId);
  63 + checkEntityId(toId);
  64 + try {
  65 + Boolean found = relationService.deleteRelation(fromId, toId, strRelationType).get();
  66 + if (!found) {
  67 + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
  68 + }
  69 + } catch (Exception e) {
  70 + throw handleException(e);
  71 + }
  72 + }
  73 +
  74 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  75 + @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"id", "type"})
  76 + @ResponseStatus(value = HttpStatus.OK)
  77 + public void deleteRelations(@RequestParam("entityId") String strId,
  78 + @RequestParam("entityType") String strType) throws ThingsboardException {
  79 + checkParameter("entityId", strId);
  80 + checkParameter("entityType", strType);
  81 + EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
  82 + checkEntityId(entityId);
  83 + try {
  84 + relationService.deleteEntityRelations(entityId).get();
  85 + } catch (Exception e) {
  86 + throw handleException(e);
  87 + }
  88 + }
  89 +
  90 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  91 + @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType", "toId", "toType"})
  92 + @ResponseStatus(value = HttpStatus.OK)
  93 + public void checkRelation(@RequestParam("fromId") String strFromId,
  94 + @RequestParam("fromType") String strFromType, @RequestParam("relationType") String strRelationType,
  95 + @RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  96 + try {
  97 + checkParameter("fromId", strFromId);
  98 + checkParameter("fromType", strFromType);
  99 + checkParameter("relationType", strRelationType);
  100 + checkParameter("toId", strToId);
  101 + checkParameter("toType", strToType);
  102 + EntityId fromId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  103 + EntityId toId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  104 + checkEntityId(fromId);
  105 + checkEntityId(toId);
  106 + Boolean found = relationService.checkRelation(fromId, toId, strRelationType).get();
  107 + if (!found) {
  108 + throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
  109 + }
  110 + } catch (Exception e) {
  111 + throw handleException(e);
  112 + }
  113 + }
  114 +
  115 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  116 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType"})
  117 + @ResponseBody
  118 + public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException {
  119 + checkParameter("fromId", strFromId);
  120 + checkParameter("fromType", strFromType);
  121 + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  122 + checkEntityId(entityId);
  123 + try {
  124 + return checkNotNull(relationService.findByFrom(entityId).get());
  125 + } catch (Exception e) {
  126 + throw handleException(e);
  127 + }
  128 + }
  129 +
  130 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  131 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
  132 + @ResponseBody
  133 + public List<EntityRelation> findByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType
  134 + , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
  135 + checkParameter("fromId", strFromId);
  136 + checkParameter("fromType", strFromType);
  137 + checkParameter("relationType", strRelationType);
  138 + EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
  139 + checkEntityId(entityId);
  140 + try {
  141 + return checkNotNull(relationService.findByFromAndType(entityId, strRelationType).get());
  142 + } catch (Exception e) {
  143 + throw handleException(e);
  144 + }
  145 + }
  146 +
  147 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  148 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType"})
  149 + @ResponseBody
  150 + public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType) throws ThingsboardException {
  151 + checkParameter("toId", strToId);
  152 + checkParameter("toType", strToType);
  153 + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  154 + checkEntityId(entityId);
  155 + try {
  156 + return checkNotNull(relationService.findByTo(entityId).get());
  157 + } catch (Exception e) {
  158 + throw handleException(e);
  159 + }
  160 + }
  161 +
  162 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  163 + @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"toId", "toType", "relationType"})
  164 + @ResponseBody
  165 + public List<EntityRelation> findByTo(@RequestParam("toId") String strToId, @RequestParam("toType") String strToType
  166 + , @RequestParam("relationType") String strRelationType) throws ThingsboardException {
  167 + checkParameter("toId", strToId);
  168 + checkParameter("toType", strToType);
  169 + checkParameter("relationType", strRelationType);
  170 + EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
  171 + checkEntityId(entityId);
  172 + try {
  173 + return checkNotNull(relationService.findByToAndType(entityId, strRelationType).get());
  174 + } catch (Exception e) {
  175 + throw handleException(e);
  176 + }
  177 + }
  178 +
  179 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  180 + @RequestMapping(value = "/relations", method = RequestMethod.POST)
  181 + @ResponseBody
  182 + public List<EntityRelation> findByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException {
  183 + checkNotNull(query);
  184 + checkNotNull(query.getParameters());
  185 + checkNotNull(query.getFilters());
  186 + checkEntityId(query.getParameters().getEntityId());
  187 + try {
  188 + return checkNotNull(relationService.findByQuery(query).get());
  189 + } catch (Exception e) {
  190 + throw handleException(e);
  191 + }
  192 + }
  193 +
  194 +}
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.controller;
18 18 import org.springframework.beans.factory.annotation.Autowired;
19 19 import org.springframework.security.access.prepost.PreAuthorize;
20 20 import org.springframework.web.bind.annotation.*;
21   -import org.thingsboard.server.common.data.EntityType;
22 21 import org.thingsboard.server.common.data.Event;
23 22 import org.thingsboard.server.common.data.id.*;
24 23 import org.thingsboard.server.common.data.page.TimePageData;
... ... @@ -59,7 +58,7 @@ public class EventController extends BaseController {
59 58 ThingsboardErrorCode.PERMISSION_DENIED);
60 59 }
61 60 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
62   - return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), eventType, pageLink));
  61 + return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), eventType, pageLink));
63 62 } catch (Exception e) {
64 63 throw handleException(e);
65 64 }
... ... @@ -88,29 +87,10 @@ public class EventController extends BaseController {
88 87 ThingsboardErrorCode.PERMISSION_DENIED);
89 88 }
90 89 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
91   - return checkNotNull(eventService.findEvents(tenantId, getEntityId(strEntityType, strEntityId), pageLink));
  90 + return checkNotNull(eventService.findEvents(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), pageLink));
92 91 } catch (Exception e) {
93 92 throw handleException(e);
94 93 }
95 94 }
96 95
97   -
98   - private EntityId getEntityId(String strEntityType, String strEntityId) throws ThingsboardException {
99   - EntityId entityId;
100   - EntityType entityType = EntityType.valueOf(strEntityType);
101   - switch (entityType) {
102   - case RULE:
103   - entityId = new RuleId(toUUID(strEntityId));
104   - break;
105   - case PLUGIN:
106   - entityId = new PluginId(toUUID(strEntityId));
107   - break;
108   - case DEVICE:
109   - entityId = new DeviceId(toUUID(strEntityId));
110   - break;
111   - default:
112   - throw new ThingsboardException("EntityType ['" + entityType + "'] is incorrect!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
113   - }
114   - return entityId;
115   - }
116 96 }
... ...
... ... @@ -15,11 +15,13 @@
15 15 */
16 16 package org.thingsboard.server.service.cluster.routing;
17 17
  18 +import org.thingsboard.server.common.data.id.EntityId;
18 19 import org.thingsboard.server.common.data.id.UUIDBased;
19 20 import org.thingsboard.server.common.msg.cluster.ServerAddress;
20 21 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
21 22
22 23 import java.util.Optional;
  24 +import java.util.UUID;
23 25
24 26 /**
25 27 * @author Andrew Shvayka
... ... @@ -28,6 +30,8 @@ public interface ClusterRoutingService {
28 30
29 31 ServerAddress getCurrentServer();
30 32
31   - Optional<ServerAddress> resolve(UUIDBased entityId);
  33 + Optional<ServerAddress> resolveByUuid(UUID uuid);
  34 +
  35 + Optional<ServerAddress> resolveById(EntityId entityId);
32 36
33 37 }
... ...
... ... @@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
22 22 import org.springframework.beans.factory.annotation.Value;
23 23 import org.springframework.stereotype.Service;
24 24 import org.springframework.util.Assert;
  25 +import org.thingsboard.server.common.data.id.EntityId;
25 26 import org.thingsboard.server.common.data.id.UUIDBased;
26 27 import org.thingsboard.server.common.msg.cluster.ServerAddress;
27 28 import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
... ... @@ -31,6 +32,7 @@ import org.thingsboard.server.utils.MiscUtils;
31 32
32 33 import javax.annotation.PostConstruct;
33 34 import java.util.Optional;
  35 +import java.util.UUID;
34 36 import java.util.concurrent.ConcurrentNavigableMap;
35 37 import java.util.concurrent.ConcurrentSkipListMap;
36 38
... ... @@ -77,13 +79,18 @@ public class ConsistentClusterRoutingService implements ClusterRoutingService, D
77 79 }
78 80
79 81 @Override
80   - public Optional<ServerAddress> resolve(UUIDBased entityId) {
81   - Assert.notNull(entityId);
  82 + public Optional<ServerAddress> resolveById(EntityId entityId) {
  83 + return resolveByUuid(entityId.getId());
  84 + }
  85 +
  86 + @Override
  87 + public Optional<ServerAddress> resolveByUuid(UUID uuid) {
  88 + Assert.notNull(uuid);
82 89 if (circle.isEmpty()) {
83 90 return Optional.empty();
84 91 }
85   - Long hash = hashFunction.newHasher().putLong(entityId.getId().getMostSignificantBits())
86   - .putLong(entityId.getId().getLeastSignificantBits()).hash().asLong();
  92 + Long hash = hashFunction.newHasher().putLong(uuid.getMostSignificantBits())
  93 + .putLong(uuid.getLeastSignificantBits()).hash().asLong();
87 94 if (!circle.containsKey(hash)) {
88 95 ConcurrentNavigableMap<Long, ServerInstance> tailMap =
89 96 circle.tailMap(hash);
... ...
... ... @@ -60,8 +60,8 @@ plugins:
60 60
61 61 # JWT Token parameters
62 62 security.jwt:
63   - tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:900}" # Number of seconds (15 mins)
64   - refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:3600}" # Seconds (1 hour)
  63 + tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins)
  64 + refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour)
65 65 tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}"
66 66 tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}"
67 67
... ...
... ... @@ -145,7 +145,7 @@ public class DefaultActorServiceTest {
145 145 ReflectionTestUtils.setField(actorContext, "eventService", eventService);
146 146
147 147
148   - when(routingService.resolve(any())).thenReturn(Optional.empty());
  148 + when(routingService.resolveById((EntityId) any())).thenReturn(Optional.empty());
149 149
150 150 when(discoveryService.getCurrentServer()).thenReturn(serverInstance);
151 151
... ... @@ -239,7 +239,7 @@ public class DefaultActorServiceTest {
239 239 List<TsKvEntry> expected = new ArrayList<>();
240 240 expected.add(new BasicTsKvEntry(ts, entry1));
241 241 expected.add(new BasicTsKvEntry(ts, entry2));
242   - verify(tsService, Mockito.timeout(5000)).save(DataConstants.DEVICE, deviceId, expected);
  242 + verify(tsService, Mockito.timeout(5000)).save(deviceId, expected);
243 243 }
244 244
245 245 }
... ...
... ... @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
19 19 * @author Andrew Shvayka
20 20 */
21 21 public enum EntityType {
22   - TENANT, DEVICE, CUSTOMER, RULE, PLUGIN
  22 + TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM
23 23 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.BaseData;
  21 +import org.thingsboard.server.common.data.id.AssetId;
  22 +import org.thingsboard.server.common.data.id.EntityId;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +
  25 +/**
  26 + * Created by ashvayka on 11.05.17.
  27 + */
  28 +@Data
  29 +public class Alarm extends BaseData<AlarmId> {
  30 +
  31 + private TenantId tenantId;
  32 + private String type;
  33 + private EntityId originator;
  34 + private AlarmSeverity severity;
  35 + private AlarmStatus status;
  36 + private long startTs;
  37 + private long endTs;
  38 + private long ackTs;
  39 + private long clearTs;
  40 + private JsonNode details;
  41 + private boolean propagate;
  42 +
  43 + public Alarm() {
  44 + super();
  45 + }
  46 +
  47 + public Alarm(AlarmId id) {
  48 + super(id);
  49 + }
  50 +
  51 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  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 +import org.thingsboard.server.common.data.id.EntityId;
  23 +import org.thingsboard.server.common.data.id.UUIDBased;
  24 +
  25 +import java.util.UUID;
  26 +
  27 +public class AlarmId extends UUIDBased implements EntityId {
  28 +
  29 + private static final long serialVersionUID = 1L;
  30 +
  31 + @JsonCreator
  32 + public AlarmId(@JsonProperty("id") UUID id) {
  33 + super(id);
  34 + }
  35 +
  36 + public static AlarmId fromString(String alarmId) {
  37 + return new AlarmId(UUID.fromString(alarmId));
  38 + }
  39 +
  40 + @JsonIgnore
  41 + @Override
  42 + public EntityType getEntityType() {
  43 + return EntityType.ALARM;
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.page.TimePageLink;
  22 +
  23 +/**
  24 + * Created by ashvayka on 11.05.17.
  25 + */
  26 +@Data
  27 +public class AlarmQuery {
  28 +
  29 + private TenantId tenantId;
  30 + private EntityId affectedEntityId;
  31 + private TimePageLink pageLink;
  32 + private AlarmStatus status;
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +/**
  19 + * Created by ashvayka on 11.05.17.
  20 + */
  21 +public enum AlarmSeverity {
  22 +
  23 + CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE;
  24 +
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +/**
  19 + * Created by ashvayka on 11.05.17.
  20 + */
  21 +public enum AlarmStatus {
  22 +
  23 + ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK;
  24 +
  25 + public boolean isAck() {
  26 + return this == ACTIVE_ACK || this == CLEARED_ACK;
  27 + }
  28 +
  29 + public boolean isCleared() {
  30 + return this == CLEARED_ACK || this == CLEARED_UNACK;
  31 + }
  32 +
  33 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.SearchTextBased;
  20 +import org.thingsboard.server.common.data.id.AssetId;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +
  24 +public class Asset extends SearchTextBased<AssetId> {
  25 +
  26 + private static final long serialVersionUID = 2807343040519543363L;
  27 +
  28 + private TenantId tenantId;
  29 + private CustomerId customerId;
  30 + private String name;
  31 + private String type;
  32 + private JsonNode additionalInfo;
  33 +
  34 + public Asset() {
  35 + super();
  36 + }
  37 +
  38 + public Asset(AssetId id) {
  39 + super(id);
  40 + }
  41 +
  42 + public Asset(Asset asset) {
  43 + super(asset);
  44 + this.tenantId = asset.getTenantId();
  45 + this.customerId = asset.getCustomerId();
  46 + this.name = asset.getName();
  47 + this.type = asset.getType();
  48 + this.additionalInfo = asset.getAdditionalInfo();
  49 + }
  50 +
  51 + public TenantId getTenantId() {
  52 + return tenantId;
  53 + }
  54 +
  55 + public void setTenantId(TenantId tenantId) {
  56 + this.tenantId = tenantId;
  57 + }
  58 +
  59 + public CustomerId getCustomerId() {
  60 + return customerId;
  61 + }
  62 +
  63 + public void setCustomerId(CustomerId customerId) {
  64 + this.customerId = customerId;
  65 + }
  66 +
  67 + public String getName() {
  68 + return name;
  69 + }
  70 +
  71 + public void setName(String name) {
  72 + this.name = name;
  73 + }
  74 +
  75 + public String getType() {
  76 + return type;
  77 + }
  78 +
  79 + public void setType(String type) {
  80 + this.type = type;
  81 + }
  82 +
  83 + public JsonNode getAdditionalInfo() {
  84 + return additionalInfo;
  85 + }
  86 +
  87 + public void setAdditionalInfo(JsonNode additionalInfo) {
  88 + this.additionalInfo = additionalInfo;
  89 + }
  90 +
  91 + @Override
  92 + public String getSearchText() {
  93 + return name;
  94 + }
  95 +
  96 + @Override
  97 + public int hashCode() {
  98 + final int prime = 31;
  99 + int result = super.hashCode();
  100 + result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
  101 + result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
  102 + result = prime * result + ((name == null) ? 0 : name.hashCode());
  103 + result = prime * result + ((type == null) ? 0 : type.hashCode());
  104 + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
  105 + return result;
  106 + }
  107 +
  108 + @Override
  109 + public boolean equals(Object obj) {
  110 + if (this == obj)
  111 + return true;
  112 + if (!super.equals(obj))
  113 + return false;
  114 + if (getClass() != obj.getClass())
  115 + return false;
  116 + Asset other = (Asset) obj;
  117 + if (additionalInfo == null) {
  118 + if (other.additionalInfo != null)
  119 + return false;
  120 + } else if (!additionalInfo.equals(other.additionalInfo))
  121 + return false;
  122 + if (customerId == null) {
  123 + if (other.customerId != null)
  124 + return false;
  125 + } else if (!customerId.equals(other.customerId))
  126 + return false;
  127 + if (name == null) {
  128 + if (other.name != null)
  129 + return false;
  130 + } else if (!name.equals(other.name))
  131 + return false;
  132 + if (type == null) {
  133 + if (other.type != null)
  134 + return false;
  135 + } else if (!type.equals(other.type))
  136 + return false;
  137 + if (tenantId == null) {
  138 + if (other.tenantId != null)
  139 + return false;
  140 + } else if (!tenantId.equals(other.tenantId))
  141 + return false;
  142 + return true;
  143 + }
  144 +
  145 + @Override
  146 + public String toString() {
  147 + StringBuilder builder = new StringBuilder();
  148 + builder.append("Asset [tenantId=");
  149 + builder.append(tenantId);
  150 + builder.append(", customerId=");
  151 + builder.append(customerId);
  152 + builder.append(", name=");
  153 + builder.append(name);
  154 + builder.append(", type=");
  155 + builder.append(type);
  156 + builder.append(", additionalInfo=");
  157 + builder.append(additionalInfo);
  158 + builder.append(", createdTime=");
  159 + builder.append(createdTime);
  160 + builder.append(", id=");
  161 + builder.append(id);
  162 + builder.append("]");
  163 + return builder.toString();
  164 + }
  165 +
  166 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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 AssetId extends UUIDBased implements EntityId {
  26 +
  27 + private static final long serialVersionUID = 1L;
  28 +
  29 + @JsonCreator
  30 + public AssetId(@JsonProperty("id") UUID id) {
  31 + super(id);
  32 + }
  33 +
  34 + public static AssetId fromString(String assetId) {
  35 + return new AssetId(UUID.fromString(assetId));
  36 + }
  37 +
  38 + @JsonIgnore
  39 + @Override
  40 + public EntityType getEntityType() {
  41 + return EntityType.ASSET;
  42 + }
  43 +}
... ...
... ... @@ -18,12 +18,24 @@ package org.thingsboard.server.common.data.id;
18 18 import java.util.UUID;
19 19
20 20 import com.fasterxml.jackson.annotation.JsonCreator;
  21 +import com.fasterxml.jackson.annotation.JsonIgnore;
21 22 import com.fasterxml.jackson.annotation.JsonProperty;
  23 +import org.thingsboard.server.common.data.EntityType;
22 24
23   -public class DashboardId extends UUIDBased {
  25 +public class DashboardId extends UUIDBased implements EntityId {
24 26
25 27 @JsonCreator
26   - public DashboardId(@JsonProperty("id") UUID id){
  28 + public DashboardId(@JsonProperty("id") UUID id) {
27 29 super(id);
28 30 }
  31 +
  32 + public static DashboardId fromString(String dashboardId) {
  33 + return new DashboardId(UUID.fromString(dashboardId));
  34 + }
  35 +
  36 + @JsonIgnore
  37 + @Override
  38 + public EntityType getEntityType() {
  39 + return EntityType.DASHBOARD;
  40 + }
29 41 }
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.server.common.data.id;
17 17
18 18 import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
  20 +import com.fasterxml.jackson.databind.annotation.JsonSerialize;
19 21 import org.thingsboard.server.common.data.EntityType;
20 22
21 23 import java.util.UUID;
... ... @@ -23,6 +25,9 @@ import java.util.UUID;
23 25 /**
24 26 * @author Andrew Shvayka
25 27 */
  28 +
  29 +@JsonDeserialize(using = EntityIdDeserializer.class)
  30 +@JsonSerialize(using = EntityIdSerializer.class)
26 31 public interface EntityId {
27 32
28 33 UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080");
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.core.JsonParser;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import com.fasterxml.jackson.core.ObjectCodec;
  21 +import com.fasterxml.jackson.databind.DeserializationContext;
  22 +import com.fasterxml.jackson.databind.JsonDeserializer;
  23 +import com.fasterxml.jackson.databind.node.ObjectNode;
  24 +
  25 +import java.io.IOException;
  26 +
  27 +/**
  28 + * Created by ashvayka on 11.05.17.
  29 + */
  30 +public class EntityIdDeserializer extends JsonDeserializer<EntityId> {
  31 +
  32 + @Override
  33 + public EntityId deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException, JsonProcessingException {
  34 + ObjectCodec oc = jsonParser.getCodec();
  35 + ObjectNode node = oc.readTree(jsonParser);
  36 + if (node.has("entityType") && node.has("id")) {
  37 + return EntityIdFactory.getByTypeAndId(node.get("entityType").asText(), node.get("id").asText());
  38 + } else {
  39 + throw new IOException("Missing entityType or id!");
  40 + }
  41 + }
  42 +
  43 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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 org.thingsboard.server.common.data.EntityType;
  19 +
  20 +import java.util.UUID;
  21 +
  22 +/**
  23 + * Created by ashvayka on 25.04.17.
  24 + */
  25 +public class EntityIdFactory {
  26 +
  27 + public static EntityId getByTypeAndId(String type, String uuid) {
  28 + return getByTypeAndUuid(EntityType.valueOf(type), UUID.fromString(uuid));
  29 + }
  30 +
  31 + public static EntityId getByTypeAndUuid(String type, UUID uuid) {
  32 + return getByTypeAndUuid(EntityType.valueOf(type), uuid);
  33 + }
  34 +
  35 + public static EntityId getByTypeAndUuid(EntityType type, UUID uuid) {
  36 + switch (type) {
  37 + case TENANT:
  38 + return new TenantId(uuid);
  39 + case CUSTOMER:
  40 + return new CustomerId(uuid);
  41 + case USER:
  42 + return new UserId(uuid);
  43 + case RULE:
  44 + return new RuleId(uuid);
  45 + case PLUGIN:
  46 + return new PluginId(uuid);
  47 + case DASHBOARD:
  48 + return new DashboardId(uuid);
  49 + case DEVICE:
  50 + return new DeviceId(uuid);
  51 + case ASSET:
  52 + return new AssetId(uuid);
  53 + }
  54 + throw new IllegalArgumentException("EntityType " + type + " is not supported!");
  55 + }
  56 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.core.JsonGenerator;
  19 +import com.fasterxml.jackson.core.JsonProcessingException;
  20 +import com.fasterxml.jackson.databind.JsonSerializer;
  21 +import com.fasterxml.jackson.databind.SerializerProvider;
  22 +
  23 +import java.io.IOException;
  24 +
  25 +/**
  26 + * Created by ashvayka on 11.05.17.
  27 + */
  28 +public class EntityIdSerializer extends JsonSerializer<EntityId> {
  29 +
  30 + @Override
  31 + public void serialize(EntityId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
  32 + gen.writeStartObject();
  33 + gen.writeStringField("entityType", value.getEntityType().name());
  34 + gen.writeStringField("id", value.getId().toString());
  35 + gen.writeEndObject();
  36 + }
  37 +}
... ...
... ... @@ -18,12 +18,25 @@ package org.thingsboard.server.common.data.id;
18 18 import java.util.UUID;
19 19
20 20 import com.fasterxml.jackson.annotation.JsonCreator;
  21 +import com.fasterxml.jackson.annotation.JsonIgnore;
21 22 import com.fasterxml.jackson.annotation.JsonProperty;
  23 +import org.thingsboard.server.common.data.EntityType;
22 24
23   -public class UserId extends UUIDBased {
  25 +public class UserId extends UUIDBased implements EntityId {
24 26
25 27 @JsonCreator
26   - public UserId(@JsonProperty("id") UUID id){
27   - super(id);
28   - }
  28 + public UserId(@JsonProperty("id") UUID id) {
  29 + super(id);
  30 + }
  31 +
  32 + public static UserId fromString(String userId) {
  33 + return new UserId(UUID.fromString(userId));
  34 + }
  35 +
  36 + @JsonIgnore
  37 + @Override
  38 + public EntityType getEntityType() {
  39 + return EntityType.USER;
  40 + }
  41 +
29 42 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +
  21 +import java.util.Objects;
  22 +
  23 +public class EntityRelation {
  24 +
  25 + private static final long serialVersionUID = 2807343040519543363L;
  26 +
  27 + public static final String CONTAINS_TYPE = "Contains";
  28 + public static final String MANAGES_TYPE = "Manages";
  29 +
  30 + private EntityId from;
  31 + private EntityId to;
  32 + private String type;
  33 + private JsonNode additionalInfo;
  34 +
  35 + public EntityRelation() {
  36 + super();
  37 + }
  38 +
  39 + public EntityRelation(EntityId from, EntityId to, String type) {
  40 + this(from, to, type, null);
  41 + }
  42 +
  43 + public EntityRelation(EntityId from, EntityId to, String type, JsonNode additionalInfo) {
  44 + this.from = from;
  45 + this.to = to;
  46 + this.type = type;
  47 + this.additionalInfo = additionalInfo;
  48 + }
  49 +
  50 + public EntityRelation(EntityRelation device) {
  51 + this.from = device.getFrom();
  52 + this.to = device.getTo();
  53 + this.type = device.getType();
  54 + this.additionalInfo = device.getAdditionalInfo();
  55 + }
  56 +
  57 + public EntityId getFrom() {
  58 + return from;
  59 + }
  60 +
  61 + public void setFrom(EntityId from) {
  62 + this.from = from;
  63 + }
  64 +
  65 + public EntityId getTo() {
  66 + return to;
  67 + }
  68 +
  69 + public void setTo(EntityId to) {
  70 + this.to = to;
  71 + }
  72 +
  73 + public String getType() {
  74 + return type;
  75 + }
  76 +
  77 + public void setType(String type) {
  78 + this.type = type;
  79 + }
  80 +
  81 + public JsonNode getAdditionalInfo() {
  82 + return additionalInfo;
  83 + }
  84 +
  85 + public void setAdditionalInfo(JsonNode additionalInfo) {
  86 + this.additionalInfo = additionalInfo;
  87 + }
  88 +
  89 + @Override
  90 + public boolean equals(Object o) {
  91 + if (this == o) return true;
  92 + if (o == null || getClass() != o.getClass()) return false;
  93 + EntityRelation relation = (EntityRelation) o;
  94 + return Objects.equals(from, relation.from) &&
  95 + Objects.equals(to, relation.to) &&
  96 + Objects.equals(type, relation.type);
  97 + }
  98 +
  99 + @Override
  100 + public int hashCode() {
  101 + return Objects.hash(from, to, type);
  102 + }
  103 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.dao.Dao;
  23 +import org.thingsboard.server.dao.model.AlarmEntity;
  24 +
  25 +/**
  26 + * Created by ashvayka on 11.05.17.
  27 + */
  28 +public interface AlarmDao extends Dao<AlarmEntity> {
  29 +
  30 + ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
  31 +
  32 + AlarmEntity save(Alarm alarm);
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.stereotype.Component;
  21 +import org.thingsboard.server.common.data.alarm.Alarm;
  22 +import org.thingsboard.server.common.data.id.EntityId;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.dao.AbstractModelDao;
  25 +import org.thingsboard.server.dao.model.AlarmEntity;
  26 +
  27 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME;
  28 +
  29 +@Component
  30 +@Slf4j
  31 +public class AlarmDaoImpl extends AbstractModelDao<AlarmEntity> implements AlarmDao {
  32 +
  33 + @Override
  34 + protected Class<AlarmEntity> getColumnFamilyClass() {
  35 + return AlarmEntity.class;
  36 + }
  37 +
  38 + @Override
  39 + protected String getColumnFamilyName() {
  40 + return ALARM_COLUMN_FAMILY_NAME;
  41 + }
  42 +
  43 + @Override
  44 + public AlarmEntity save(Alarm alarm) {
  45 + log.debug("Save asset [{}] ", alarm);
  46 + return save(new AlarmEntity(alarm));
  47 + }
  48 +
  49 + @Override
  50 + public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
  51 + return null;
  52 + }
  53 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.alarm.AlarmId;
  21 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
  22 +import org.thingsboard.server.common.data.id.EntityId;
  23 +import org.thingsboard.server.common.data.page.TimePageData;
  24 +
  25 +import java.util.Optional;
  26 +
  27 +/**
  28 + * Created by ashvayka on 11.05.17.
  29 + */
  30 +public interface AlarmService {
  31 +
  32 + Alarm createOrUpdateAlarm(Alarm alarm);
  33 +
  34 + ListenableFuture<Boolean> updateAlarm(Alarm alarm);
  35 +
  36 + ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTs);
  37 +
  38 + ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long ackTs);
  39 +
  40 + ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query);
  41 +
  42 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +
  19 +import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.AsyncFunction;
  21 +import com.google.common.util.concurrent.Futures;
  22 +import com.google.common.util.concurrent.ListenableFuture;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.stereotype.Service;
  26 +import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.common.data.EntityType;
  28 +import org.thingsboard.server.common.data.alarm.Alarm;
  29 +import org.thingsboard.server.common.data.alarm.AlarmId;
  30 +import org.thingsboard.server.common.data.alarm.AlarmQuery;
  31 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  32 +import org.thingsboard.server.common.data.asset.Asset;
  33 +import org.thingsboard.server.common.data.id.AssetId;
  34 +import org.thingsboard.server.common.data.id.CustomerId;
  35 +import org.thingsboard.server.common.data.id.EntityId;
  36 +import org.thingsboard.server.common.data.id.TenantId;
  37 +import org.thingsboard.server.common.data.page.TextPageData;
  38 +import org.thingsboard.server.common.data.page.TextPageLink;
  39 +import org.thingsboard.server.common.data.page.TimePageData;
  40 +import org.thingsboard.server.common.data.relation.EntityRelation;
  41 +import org.thingsboard.server.dao.asset.AssetDao;
  42 +import org.thingsboard.server.dao.asset.AssetSearchQuery;
  43 +import org.thingsboard.server.dao.asset.AssetService;
  44 +import org.thingsboard.server.dao.customer.CustomerDao;
  45 +import org.thingsboard.server.dao.entity.BaseEntityService;
  46 +import org.thingsboard.server.dao.exception.DataValidationException;
  47 +import org.thingsboard.server.dao.model.AlarmEntity;
  48 +import org.thingsboard.server.dao.model.AssetEntity;
  49 +import org.thingsboard.server.dao.model.CustomerEntity;
  50 +import org.thingsboard.server.dao.model.TenantEntity;
  51 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  52 +import org.thingsboard.server.dao.relation.EntitySearchDirection;
  53 +import org.thingsboard.server.dao.relation.RelationService;
  54 +import org.thingsboard.server.dao.relation.RelationsSearchParameters;
  55 +import org.thingsboard.server.dao.service.DataValidator;
  56 +import org.thingsboard.server.dao.service.PaginatedRemover;
  57 +import org.thingsboard.server.dao.tenant.TenantDao;
  58 +
  59 +import javax.annotation.Nullable;
  60 +import java.util.ArrayList;
  61 +import java.util.Collections;
  62 +import java.util.List;
  63 +import java.util.Optional;
  64 +import java.util.concurrent.ExecutionException;
  65 +import java.util.stream.Collectors;
  66 +
  67 +import static org.thingsboard.server.dao.DaoUtil.*;
  68 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
  69 +import static org.thingsboard.server.dao.service.Validator.*;
  70 +
  71 +@Service
  72 +@Slf4j
  73 +public class BaseAlarmService extends BaseEntityService implements AlarmService {
  74 +
  75 + private static final String ALARM_RELATION_PREFIX = "ALARM_";
  76 + private static final String ALARM_RELATION = "ALARM_ANY";
  77 +
  78 + @Autowired
  79 + private AlarmDao alarmDao;
  80 +
  81 + @Autowired
  82 + private TenantDao tenantDao;
  83 +
  84 + @Autowired
  85 + private RelationService relationService;
  86 +
  87 + @Override
  88 + public Alarm createOrUpdateAlarm(Alarm alarm) {
  89 + alarmDataValidator.validate(alarm);
  90 + try {
  91 + if (alarm.getStartTs() == 0L) {
  92 + alarm.setStartTs(System.currentTimeMillis());
  93 + }
  94 + Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
  95 + if (existing == null || existing.getStatus().isCleared()) {
  96 + log.debug("New Alarm : {}", alarm);
  97 + Alarm saved = getData(alarmDao.save(new AlarmEntity(alarm)));
  98 + EntityRelationsQuery query = new EntityRelationsQuery();
  99 + query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
  100 + List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
  101 + for (EntityId parentId : parentEntities) {
  102 + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION));
  103 + createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
  104 + }
  105 + return saved;
  106 + } else {
  107 + log.debug("Alarm before merge: {}", alarm);
  108 + alarm = merge(existing, alarm);
  109 + log.debug("Alarm after merge: {}", alarm);
  110 + return getData(alarmDao.save(new AlarmEntity(alarm)));
  111 + }
  112 + } catch (ExecutionException | InterruptedException e) {
  113 + throw new RuntimeException(e);
  114 + }
  115 + }
  116 +
  117 + @Override
  118 + public ListenableFuture<Boolean> updateAlarm(Alarm update) {
  119 + alarmDataValidator.validate(update);
  120 + return getAndUpdate(update.getId(), new Function<AlarmEntity, Boolean>() {
  121 + @Nullable
  122 + @Override
  123 + public Boolean apply(@Nullable AlarmEntity entity) {
  124 + Alarm alarm = getData(entity);
  125 + if (alarm == null) {
  126 + return false;
  127 + } else {
  128 + AlarmStatus oldStatus = alarm.getStatus();
  129 + AlarmStatus newStatus = update.getStatus();
  130 + alarmDao.save(new AlarmEntity(merge(alarm, update)));
  131 + if (oldStatus != newStatus) {
  132 + updateRelations(alarm, oldStatus, newStatus);
  133 + }
  134 + return true;
  135 + }
  136 + }
  137 + });
  138 + }
  139 +
  140 + @Override
  141 + public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) {
  142 + return getAndUpdate(alarmId, new Function<AlarmEntity, Boolean>() {
  143 + @Nullable
  144 + @Override
  145 + public Boolean apply(@Nullable AlarmEntity entity) {
  146 + Alarm alarm = getData(entity);
  147 + if (alarm == null || alarm.getStatus().isAck()) {
  148 + return false;
  149 + } else {
  150 + AlarmStatus oldStatus = alarm.getStatus();
  151 + AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
  152 + alarm.setStatus(newStatus);
  153 + alarm.setAckTs(ackTime);
  154 + alarmDao.save(new AlarmEntity(alarm));
  155 + updateRelations(alarm, oldStatus, newStatus);
  156 + return true;
  157 + }
  158 + }
  159 + });
  160 + }
  161 +
  162 + @Override
  163 + public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long clearTime) {
  164 + return getAndUpdate(alarmId, new Function<AlarmEntity, Boolean>() {
  165 + @Nullable
  166 + @Override
  167 + public Boolean apply(@Nullable AlarmEntity entity) {
  168 + Alarm alarm = getData(entity);
  169 + if (alarm == null || alarm.getStatus().isCleared()) {
  170 + return false;
  171 + } else {
  172 + AlarmStatus oldStatus = alarm.getStatus();
  173 + AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
  174 + alarm.setStatus(newStatus);
  175 + alarm.setClearTs(clearTime);
  176 + alarmDao.save(new AlarmEntity(alarm));
  177 + updateRelations(alarm, oldStatus, newStatus);
  178 + return true;
  179 + }
  180 + }
  181 + });
  182 + }
  183 +
  184 + @Override
  185 + public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
  186 + return null;
  187 + }
  188 +
  189 + private void deleteRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
  190 + log.debug("Deleting Alarm relation: {}", alarmRelation);
  191 + relationService.deleteRelation(alarmRelation).get();
  192 + }
  193 +
  194 + private void createRelation(EntityRelation alarmRelation) throws ExecutionException, InterruptedException {
  195 + log.debug("Creating Alarm relation: {}", alarmRelation);
  196 + relationService.saveRelation(alarmRelation).get();
  197 + }
  198 +
  199 + private Alarm merge(Alarm existing, Alarm alarm) {
  200 + if (alarm.getStartTs() > existing.getEndTs()) {
  201 + existing.setEndTs(alarm.getStartTs());
  202 + }
  203 + if (alarm.getEndTs() > existing.getEndTs()) {
  204 + existing.setEndTs(alarm.getEndTs());
  205 + }
  206 + if (alarm.getClearTs() > existing.getClearTs()) {
  207 + existing.setClearTs(alarm.getClearTs());
  208 + }
  209 + if (alarm.getAckTs() > existing.getAckTs()) {
  210 + existing.setAckTs(alarm.getAckTs());
  211 + }
  212 + existing.setStatus(alarm.getStatus());
  213 + existing.setSeverity(alarm.getSeverity());
  214 + existing.setDetails(alarm.getDetails());
  215 + return existing;
  216 + }
  217 +
  218 + private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) {
  219 + try {
  220 + EntityRelationsQuery query = new EntityRelationsQuery();
  221 + query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
  222 + List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
  223 + for (EntityId parentId : parentEntities) {
  224 + deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name()));
  225 + createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name()));
  226 + }
  227 + } catch (ExecutionException | InterruptedException e) {
  228 + log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
  229 + throw new RuntimeException(e);
  230 + }
  231 + }
  232 +
  233 + private ListenableFuture<Boolean> getAndUpdate(AlarmId alarmId, Function<AlarmEntity, Boolean> function) {
  234 + validateId(alarmId, "Alarm id should be specified!");
  235 + ListenableFuture<AlarmEntity> entity = alarmDao.findByIdAsync(alarmId.getId());
  236 + return Futures.transform(entity, function);
  237 + }
  238 +
  239 + private DataValidator<Alarm> alarmDataValidator =
  240 + new DataValidator<Alarm>() {
  241 +
  242 + @Override
  243 + protected void validateDataImpl(Alarm alarm) {
  244 + if (StringUtils.isEmpty(alarm.getType())) {
  245 + throw new DataValidationException("Alarm type should be specified!");
  246 + }
  247 + if (alarm.getOriginator() == null) {
  248 + throw new DataValidationException("Alarm originator should be specified!");
  249 + }
  250 + if (alarm.getSeverity() == null) {
  251 + throw new DataValidationException("Alarm severity should be specified!");
  252 + }
  253 + if (alarm.getStatus() == null) {
  254 + throw new DataValidationException("Alarm status should be specified!");
  255 + }
  256 + if (alarm.getTenantId() == null) {
  257 + throw new DataValidationException("Alarm should be assigned to tenant!");
  258 + } else {
  259 + TenantEntity tenant = tenantDao.findById(alarm.getTenantId().getId());
  260 + if (tenant == null) {
  261 + throw new DataValidationException("Alarm is referencing to non-existent tenant!");
  262 + }
  263 + }
  264 + }
  265 + };
  266 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.Device;
  20 +import org.thingsboard.server.common.data.asset.Asset;
  21 +import org.thingsboard.server.common.data.page.TextPageLink;
  22 +import org.thingsboard.server.dao.Dao;
  23 +import org.thingsboard.server.dao.model.AssetEntity;
  24 +import org.thingsboard.server.dao.model.DeviceEntity;
  25 +
  26 +import java.util.List;
  27 +import java.util.Optional;
  28 +import java.util.UUID;
  29 +
  30 +/**
  31 + * The Interface AssetDao.
  32 + *
  33 + */
  34 +public interface AssetDao extends Dao<AssetEntity> {
  35 +
  36 + /**
  37 + * Save or update asset object
  38 + *
  39 + * @param asset the asset object
  40 + * @return saved asset object
  41 + */
  42 + AssetEntity save(Asset asset);
  43 +
  44 + /**
  45 + * Find assets by tenantId and page link.
  46 + *
  47 + * @param tenantId the tenantId
  48 + * @param pageLink the page link
  49 + * @return the list of asset objects
  50 + */
  51 + List<AssetEntity> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
  52 +
  53 + /**
  54 + * Find assets by tenantId and assets Ids.
  55 + *
  56 + * @param tenantId the tenantId
  57 + * @param assetIds the asset Ids
  58 + * @return the list of asset objects
  59 + */
  60 + ListenableFuture<List<AssetEntity>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds);
  61 +
  62 + /**
  63 + * Find assets by tenantId, customerId and page link.
  64 + *
  65 + * @param tenantId the tenantId
  66 + * @param customerId the customerId
  67 + * @param pageLink the page link
  68 + * @return the list of asset objects
  69 + */
  70 + List<AssetEntity> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
  71 +
  72 + /**
  73 + * Find assets by tenantId, customerId and assets Ids.
  74 + *
  75 + * @param tenantId the tenantId
  76 + * @param customerId the customerId
  77 + * @param assetIds the asset Ids
  78 + * @return the list of asset objects
  79 + */
  80 + ListenableFuture<List<AssetEntity>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds);
  81 +
  82 + /**
  83 + * Find assets by tenantId and asset name.
  84 + *
  85 + * @param tenantId the tenantId
  86 + * @param name the asset name
  87 + * @return the optional asset object
  88 + */
  89 + Optional<AssetEntity> findAssetsByTenantIdAndName(UUID tenantId, String name);
  90 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import com.datastax.driver.core.querybuilder.Select;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.asset.Asset;
  23 +import org.thingsboard.server.common.data.page.TextPageLink;
  24 +import org.thingsboard.server.dao.AbstractSearchTextDao;
  25 +import org.thingsboard.server.dao.model.AssetEntity;
  26 +
  27 +import java.util.*;
  28 +
  29 +import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
  30 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  31 +
  32 +@Component
  33 +@Slf4j
  34 +public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements AssetDao {
  35 +
  36 + @Override
  37 + protected Class<AssetEntity> getColumnFamilyClass() {
  38 + return AssetEntity.class;
  39 + }
  40 +
  41 + @Override
  42 + protected String getColumnFamilyName() {
  43 + return ASSET_COLUMN_FAMILY_NAME;
  44 + }
  45 +
  46 + @Override
  47 + public AssetEntity save(Asset asset) {
  48 + log.debug("Save asset [{}] ", asset);
  49 + return save(new AssetEntity(asset));
  50 + }
  51 +
  52 + @Override
  53 + public List<AssetEntity> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) {
  54 + log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
  55 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  56 + Collections.singletonList(eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
  57 +
  58 + log.trace("Found assets [{}] by tenantId [{}] and pageLink [{}]", assetEntities, tenantId, pageLink);
  59 + return assetEntities;
  60 + }
  61 +
  62 + @Override
  63 + public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds) {
  64 + log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
  65 + Select select = select().from(getColumnFamilyName());
  66 + Select.Where query = select.where();
  67 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  68 + query.and(in(ID_PROPERTY, assetIds));
  69 + return findListByStatementAsync(query);
  70 + }
  71 +
  72 + @Override
  73 + public List<AssetEntity> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
  74 + log.debug("Try to find assets by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
  75 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  76 + Arrays.asList(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
  77 + eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
  78 + pageLink);
  79 +
  80 + log.trace("Found assets [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", assetEntities, tenantId, customerId, pageLink);
  81 + return assetEntities;
  82 + }
  83 +
  84 + @Override
  85 + public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds) {
  86 + log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
  87 + Select select = select().from(getColumnFamilyName());
  88 + Select.Where query = select.where();
  89 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  90 + query.and(eq(ASSET_CUSTOMER_ID_PROPERTY, customerId));
  91 + query.and(in(ID_PROPERTY, assetIds));
  92 + return findListByStatementAsync(query);
  93 + }
  94 +
  95 + @Override
  96 + public Optional<AssetEntity> findAssetsByTenantIdAndName(UUID tenantId, String assetName) {
  97 + Select select = select().from(ASSET_BY_TENANT_AND_NAME_VIEW_NAME);
  98 + Select.Where query = select.where();
  99 + query.and(eq(ASSET_TENANT_ID_PROPERTY, tenantId));
  100 + query.and(eq(ASSET_NAME_PROPERTY, assetName));
  101 + return Optional.ofNullable(findOneByStatement(query));
  102 + }
  103 +
  104 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.EntityType;
  20 +import org.thingsboard.server.common.data.relation.EntityRelation;
  21 +import org.thingsboard.server.dao.relation.RelationsSearchParameters;
  22 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  23 +import org.thingsboard.server.dao.relation.EntityTypeFilter;
  24 +
  25 +import javax.annotation.Nullable;
  26 +import java.util.ArrayList;
  27 +import java.util.Collections;
  28 +import java.util.List;
  29 +
  30 +/**
  31 + * Created by ashvayka on 03.05.17.
  32 + */
  33 +@Data
  34 +public class AssetSearchQuery {
  35 +
  36 + private RelationsSearchParameters parameters;
  37 + @Nullable
  38 + private String relationType;
  39 + @Nullable
  40 + private List<String> assetTypes;
  41 +
  42 + public EntityRelationsQuery toEntitySearchQuery() {
  43 + EntityRelationsQuery query = new EntityRelationsQuery();
  44 + query.setParameters(parameters);
  45 + query.setFilters(
  46 + Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType,
  47 + Collections.singletonList(EntityType.ASSET))));
  48 + return query;
  49 + }
  50 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.asset.Asset;
  20 +import org.thingsboard.server.common.data.id.AssetId;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.page.TextPageData;
  24 +import org.thingsboard.server.common.data.page.TextPageLink;
  25 +
  26 +import java.util.List;
  27 +import java.util.Optional;
  28 +
  29 +public interface AssetService {
  30 +
  31 + Asset findAssetById(AssetId assetId);
  32 +
  33 + ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId);
  34 +
  35 + Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name);
  36 +
  37 + Asset saveAsset(Asset device);
  38 +
  39 + Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
  40 +
  41 + Asset unassignAssetFromCustomer(AssetId assetId);
  42 +
  43 + void deleteAsset(AssetId assetId);
  44 +
  45 + TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
  46 +
  47 + ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
  48 +
  49 + void deleteAssetsByTenantId(TenantId tenantId);
  50 +
  51 + TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
  52 +
  53 + ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);
  54 +
  55 + void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
  56 +
  57 + ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
  58 +
  59 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import javax.annotation.Nullable;
  21 +import java.util.List;
  22 +
  23 +/**
  24 + * Created by ashvayka on 02.05.17.
  25 + */
  26 +@Data
  27 +public class AssetTypeFilter {
  28 + @Nullable
  29 + private String relationType;
  30 + @Nullable
  31 + private List<String> assetTypes;
  32 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.asset;
  17 +
  18 +
  19 +import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.AsyncFunction;
  21 +import com.google.common.util.concurrent.Futures;
  22 +import com.google.common.util.concurrent.ListenableFuture;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.stereotype.Service;
  26 +import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.common.data.EntityType;
  28 +import org.thingsboard.server.common.data.asset.Asset;
  29 +import org.thingsboard.server.common.data.id.AssetId;
  30 +import org.thingsboard.server.common.data.id.CustomerId;
  31 +import org.thingsboard.server.common.data.id.EntityId;
  32 +import org.thingsboard.server.common.data.id.TenantId;
  33 +import org.thingsboard.server.common.data.page.TextPageData;
  34 +import org.thingsboard.server.common.data.page.TextPageLink;
  35 +import org.thingsboard.server.common.data.relation.EntityRelation;
  36 +import org.thingsboard.server.dao.customer.CustomerDao;
  37 +import org.thingsboard.server.dao.entity.BaseEntityService;
  38 +import org.thingsboard.server.dao.exception.DataValidationException;
  39 +import org.thingsboard.server.dao.model.AssetEntity;
  40 +import org.thingsboard.server.dao.model.CustomerEntity;
  41 +import org.thingsboard.server.dao.model.TenantEntity;
  42 +import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  43 +import org.thingsboard.server.dao.relation.EntitySearchDirection;
  44 +import org.thingsboard.server.dao.service.DataValidator;
  45 +import org.thingsboard.server.dao.service.PaginatedRemover;
  46 +import org.thingsboard.server.dao.tenant.TenantDao;
  47 +
  48 +import javax.annotation.Nullable;
  49 +import java.util.ArrayList;
  50 +import java.util.List;
  51 +import java.util.Optional;
  52 +import java.util.stream.Collectors;
  53 +
  54 +import static org.thingsboard.server.dao.DaoUtil.*;
  55 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
  56 +import static org.thingsboard.server.dao.service.Validator.*;
  57 +
  58 +@Service
  59 +@Slf4j
  60 +public class BaseAssetService extends BaseEntityService implements AssetService {
  61 +
  62 + @Autowired
  63 + private AssetDao assetDao;
  64 +
  65 + @Autowired
  66 + private TenantDao tenantDao;
  67 +
  68 + @Autowired
  69 + private CustomerDao customerDao;
  70 +
  71 + @Override
  72 + public Asset findAssetById(AssetId assetId) {
  73 + log.trace("Executing findAssetById [{}]", assetId);
  74 + validateId(assetId, "Incorrect assetId " + assetId);
  75 + AssetEntity assetEntity = assetDao.findById(assetId.getId());
  76 + return getData(assetEntity);
  77 + }
  78 +
  79 + @Override
  80 + public ListenableFuture<Asset> findAssetByIdAsync(AssetId assetId) {
  81 + log.trace("Executing findAssetById [{}]", assetId);
  82 + validateId(assetId, "Incorrect assetId " + assetId);
  83 + ListenableFuture<AssetEntity> assetEntity = assetDao.findByIdAsync(assetId.getId());
  84 + return Futures.transform(assetEntity, (Function<? super AssetEntity, ? extends Asset>) input -> getData(input));
  85 + }
  86 +
  87 + @Override
  88 + public Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name) {
  89 + log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name);
  90 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  91 + Optional<AssetEntity> assetEntityOpt = assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name);
  92 + if (assetEntityOpt.isPresent()) {
  93 + return Optional.of(getData(assetEntityOpt.get()));
  94 + } else {
  95 + return Optional.empty();
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + public Asset saveAsset(Asset asset) {
  101 + log.trace("Executing saveAsset [{}]", asset);
  102 + assetValidator.validate(asset);
  103 + return getData(assetDao.save(asset));
  104 + }
  105 +
  106 + @Override
  107 + public Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId) {
  108 + Asset asset = findAssetById(assetId);
  109 + asset.setCustomerId(customerId);
  110 + return saveAsset(asset);
  111 + }
  112 +
  113 + @Override
  114 + public Asset unassignAssetFromCustomer(AssetId assetId) {
  115 + Asset asset = findAssetById(assetId);
  116 + asset.setCustomerId(null);
  117 + return saveAsset(asset);
  118 + }
  119 +
  120 + @Override
  121 + public void deleteAsset(AssetId assetId) {
  122 + log.trace("Executing deleteAsset [{}]", assetId);
  123 + validateId(assetId, "Incorrect assetId " + assetId);
  124 + deleteEntityRelations(assetId);
  125 + assetDao.removeById(assetId.getId());
  126 + }
  127 +
  128 + @Override
  129 + public TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink) {
  130 + log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
  131 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  132 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  133 + List<AssetEntity> assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
  134 + List<Asset> assets = convertDataList(assetEntities);
  135 + return new TextPageData<Asset>(assets, pageLink);
  136 + }
  137 +
  138 + @Override
  139 + public ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds) {
  140 + log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds);
  141 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  142 + validateIds(assetIds, "Incorrect assetIds " + assetIds);
  143 + ListenableFuture<List<AssetEntity>> assetEntities = assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds));
  144 + return Futures.transform(assetEntities, (Function<List<AssetEntity>, List<Asset>>) input -> convertDataList(input));
  145 + }
  146 +
  147 + @Override
  148 + public void deleteAssetsByTenantId(TenantId tenantId) {
  149 + log.trace("Executing deleteAssetsByTenantId, tenantId [{}]", tenantId);
  150 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  151 + tenantAssetsRemover.removeEntitites(tenantId);
  152 + }
  153 +
  154 + @Override
  155 + public TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) {
  156 + log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
  157 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  158 + validateId(customerId, "Incorrect customerId " + customerId);
  159 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  160 + List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
  161 + List<Asset> assets = convertDataList(assetEntities);
  162 + return new TextPageData<Asset>(assets, pageLink);
  163 + }
  164 +
  165 + @Override
  166 + public ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds) {
  167 + log.trace("Executing findAssetsByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds);
  168 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  169 + validateId(customerId, "Incorrect customerId " + customerId);
  170 + validateIds(assetIds, "Incorrect assetIds " + assetIds);
  171 + ListenableFuture<List<AssetEntity>> assetEntities = assetDao.findAssetsByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
  172 + customerId.getId(), toUUIDs(assetIds));
  173 + return Futures.transform(assetEntities, (Function<List<AssetEntity>, List<Asset>>) input -> convertDataList(input));
  174 + }
  175 +
  176 + @Override
  177 + public void unassignCustomerAssets(TenantId tenantId, CustomerId customerId) {
  178 + log.trace("Executing unassignCustomerAssets, tenantId [{}], customerId [{}]", tenantId, customerId);
  179 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  180 + validateId(customerId, "Incorrect customerId " + customerId);
  181 + new CustomerAssetsUnassigner(tenantId).removeEntitites(customerId);
  182 + }
  183 +
  184 + @Override
  185 + public ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query) {
  186 + ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
  187 + ListenableFuture<List<Asset>> assets = Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Asset>>) relations1 -> {
  188 + EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
  189 + List<ListenableFuture<Asset>> futures = new ArrayList<>();
  190 + for (EntityRelation relation : relations1) {
  191 + EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
  192 + if (entityId.getEntityType() == EntityType.ASSET) {
  193 + futures.add(findAssetByIdAsync(new AssetId(entityId.getId())));
  194 + }
  195 + }
  196 + return Futures.successfulAsList(futures);
  197 + });
  198 +
  199 + assets = Futures.transform(assets, new Function<List<Asset>, List<Asset>>() {
  200 + @Nullable
  201 + @Override
  202 + public List<Asset> apply(@Nullable List<Asset> assetList) {
  203 + return assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList());
  204 + }
  205 + });
  206 +
  207 + return assets;
  208 + }
  209 +
  210 + private DataValidator<Asset> assetValidator =
  211 + new DataValidator<Asset>() {
  212 +
  213 + @Override
  214 + protected void validateCreate(Asset asset) {
  215 + assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
  216 + d -> {
  217 + throw new DataValidationException("Asset with such name already exists!");
  218 + }
  219 + );
  220 + }
  221 +
  222 + @Override
  223 + protected void validateUpdate(Asset asset) {
  224 + assetDao.findAssetsByTenantIdAndName(asset.getTenantId().getId(), asset.getName()).ifPresent(
  225 + d -> {
  226 + if (!d.getId().equals(asset.getUuidId())) {
  227 + throw new DataValidationException("Asset with such name already exists!");
  228 + }
  229 + }
  230 + );
  231 + }
  232 +
  233 + @Override
  234 + protected void validateDataImpl(Asset asset) {
  235 + if (StringUtils.isEmpty(asset.getName())) {
  236 + throw new DataValidationException("Asset name should be specified!");
  237 + }
  238 + if (asset.getTenantId() == null) {
  239 + throw new DataValidationException("Asset should be assigned to tenant!");
  240 + } else {
  241 + TenantEntity tenant = tenantDao.findById(asset.getTenantId().getId());
  242 + if (tenant == null) {
  243 + throw new DataValidationException("Asset is referencing to non-existent tenant!");
  244 + }
  245 + }
  246 + if (asset.getCustomerId() == null) {
  247 + asset.setCustomerId(new CustomerId(NULL_UUID));
  248 + } else if (!asset.getCustomerId().getId().equals(NULL_UUID)) {
  249 + CustomerEntity customer = customerDao.findById(asset.getCustomerId().getId());
  250 + if (customer == null) {
  251 + throw new DataValidationException("Can't assign asset to non-existent customer!");
  252 + }
  253 + if (!customer.getTenantId().equals(asset.getTenantId().getId())) {
  254 + throw new DataValidationException("Can't assign asset to customer from different tenant!");
  255 + }
  256 + }
  257 + }
  258 + };
  259 +
  260 + private PaginatedRemover<TenantId, AssetEntity> tenantAssetsRemover =
  261 + new PaginatedRemover<TenantId, AssetEntity>() {
  262 +
  263 + @Override
  264 + protected List<AssetEntity> findEntities(TenantId id, TextPageLink pageLink) {
  265 + return assetDao.findAssetsByTenantId(id.getId(), pageLink);
  266 + }
  267 +
  268 + @Override
  269 + protected void removeEntity(AssetEntity entity) {
  270 + deleteAsset(new AssetId(entity.getId()));
  271 + }
  272 + };
  273 +
  274 + class CustomerAssetsUnassigner extends PaginatedRemover<CustomerId, AssetEntity> {
  275 +
  276 + private TenantId tenantId;
  277 +
  278 + CustomerAssetsUnassigner(TenantId tenantId) {
  279 + this.tenantId = tenantId;
  280 + }
  281 +
  282 + @Override
  283 + protected List<AssetEntity> findEntities(CustomerId id, TextPageLink pageLink) {
  284 + return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
  285 + }
  286 +
  287 + @Override
  288 + protected void removeEntity(AssetEntity entity) {
  289 + unassignAssetFromCustomer(new AssetId(entity.getId()));
  290 + }
  291 + }
  292 +}
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.customer;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.Customer;
19 20 import org.thingsboard.server.common.data.id.CustomerId;
20 21 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -23,16 +24,18 @@ import org.thingsboard.server.common.data.page.TextPageLink;
23 24
24 25 public interface CustomerService {
25 26
26   - public Customer findCustomerById(CustomerId customerId);
  27 + Customer findCustomerById(CustomerId customerId);
  28 +
  29 + ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
27 30
28   - public Customer saveCustomer(Customer customer);
  31 + Customer saveCustomer(Customer customer);
29 32
30   - public void deleteCustomer(CustomerId customerId);
  33 + void deleteCustomer(CustomerId customerId);
31 34
32   - public Customer findOrCreatePublicCustomer(TenantId tenantId);
33   -
34   - public TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
  35 + Customer findOrCreatePublicCustomer(TenantId tenantId);
35 36
36   - public void deleteCustomersByTenantId(TenantId tenantId);
  37 + TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
37 38
  39 + void deleteCustomersByTenantId(TenantId tenantId);
  40 +
38 41 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.customer;
17 17
18 18 import static org.thingsboard.server.dao.DaoUtil.convertDataList;
19 19 import static org.thingsboard.server.dao.DaoUtil.getData;
  20 +import static org.thingsboard.server.dao.service.Validator.validateId;
20 21
21 22 import java.io.IOException;
22 23 import java.util.List;
... ... @@ -24,31 +25,35 @@ import java.util.Optional;
24 25
25 26 import com.fasterxml.jackson.databind.JsonNode;
26 27 import com.fasterxml.jackson.databind.ObjectMapper;
  28 +import com.google.common.base.Function;
  29 +import com.google.common.util.concurrent.Futures;
  30 +import com.google.common.util.concurrent.ListenableFuture;
27 31 import lombok.extern.slf4j.Slf4j;
28 32 import org.apache.commons.lang3.StringUtils;
29 33 import org.thingsboard.server.common.data.Customer;
  34 +import org.thingsboard.server.common.data.asset.Asset;
30 35 import org.thingsboard.server.common.data.id.CustomerId;
31 36 import org.thingsboard.server.common.data.id.TenantId;
32 37 import org.thingsboard.server.common.data.page.TextPageData;
33 38 import org.thingsboard.server.common.data.page.TextPageLink;
34 39 import org.thingsboard.server.dao.dashboard.DashboardService;
35 40 import org.thingsboard.server.dao.device.DeviceService;
  41 +import org.thingsboard.server.dao.entity.BaseEntityService;
36 42 import org.thingsboard.server.dao.exception.DataValidationException;
37 43 import org.thingsboard.server.dao.exception.IncorrectParameterException;
  44 +import org.thingsboard.server.dao.model.AssetEntity;
38 45 import org.thingsboard.server.dao.model.CustomerEntity;
39 46 import org.thingsboard.server.dao.model.TenantEntity;
40 47 import org.thingsboard.server.dao.service.DataValidator;
41 48 import org.thingsboard.server.dao.service.PaginatedRemover;
42 49 import org.thingsboard.server.dao.tenant.TenantDao;
43 50 import org.thingsboard.server.dao.user.UserService;
44   -import org.slf4j.Logger;
45   -import org.slf4j.LoggerFactory;
46 51 import org.springframework.beans.factory.annotation.Autowired;
47 52 import org.springframework.stereotype.Service;
48 53 import org.thingsboard.server.dao.service.Validator;
49 54 @Service
50 55 @Slf4j
51   -public class CustomerServiceImpl implements CustomerService {
  56 +public class CustomerServiceImpl extends BaseEntityService implements CustomerService {
52 57
53 58 private static final String PUBLIC_CUSTOMER_TITLE = "Public";
54 59
... ... @@ -76,6 +81,14 @@ public class CustomerServiceImpl implements CustomerService {
76 81 }
77 82
78 83 @Override
  84 + public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
  85 + log.trace("Executing findCustomerByIdAsync [{}]", customerId);
  86 + validateId(customerId, "Incorrect customerId " + customerId);
  87 + ListenableFuture<CustomerEntity> customerEntity = customerDao.findByIdAsync(customerId.getId());
  88 + return Futures.transform(customerEntity, (Function<? super CustomerEntity, ? extends Customer>) input -> getData(input));
  89 + }
  90 +
  91 + @Override
79 92 public Customer saveCustomer(Customer customer) {
80 93 log.trace("Executing saveCustomer [{}]", customer);
81 94 customerValidator.validate(customer);
... ... @@ -93,7 +106,8 @@ public class CustomerServiceImpl implements CustomerService {
93 106 }
94 107 dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId);
95 108 deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
96   - userService.deleteCustomerUsers(customer.getTenantId(), customerId);
  109 + userService.deleteCustomerUsers(customer.getTenantId(), customerId);
  110 + deleteEntityRelations(customerId);
97 111 customerDao.removeById(customerId.getId());
98 112 }
99 113
... ...
... ... @@ -30,20 +30,19 @@ import org.thingsboard.server.common.data.id.TenantId;
30 30 import org.thingsboard.server.common.data.page.TextPageData;
31 31 import org.thingsboard.server.common.data.page.TextPageLink;
32 32 import org.thingsboard.server.dao.customer.CustomerDao;
  33 +import org.thingsboard.server.dao.entity.BaseEntityService;
33 34 import org.thingsboard.server.dao.exception.DataValidationException;
34 35 import org.thingsboard.server.dao.model.*;
35 36 import org.thingsboard.server.dao.service.DataValidator;
36 37 import org.thingsboard.server.dao.service.PaginatedRemover;
37 38 import org.thingsboard.server.dao.tenant.TenantDao;
38   -import org.slf4j.Logger;
39   -import org.slf4j.LoggerFactory;
40 39 import org.springframework.beans.factory.annotation.Autowired;
41 40 import org.springframework.stereotype.Service;
42 41 import org.thingsboard.server.dao.service.Validator;
43 42
44 43 @Service
45 44 @Slf4j
46   -public class DashboardServiceImpl implements DashboardService {
  45 +public class DashboardServiceImpl extends BaseEntityService implements DashboardService {
47 46
48 47 @Autowired
49 48 private DashboardDao dashboardDao;
... ... @@ -91,6 +90,7 @@ public class DashboardServiceImpl implements DashboardService {
91 90 public void deleteDashboard(DashboardId dashboardId) {
92 91 log.trace("Executing deleteDashboard [{}]", dashboardId);
93 92 Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
  93 + deleteEntityRelations(dashboardId);
94 94 dashboardDao.removeById(dashboardId.getId());
95 95 }
96 96
... ...
... ... @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
32 32 import org.thingsboard.server.common.data.security.DeviceCredentials;
33 33 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
34 34 import org.thingsboard.server.dao.customer.CustomerDao;
  35 +import org.thingsboard.server.dao.entity.BaseEntityService;
35 36 import org.thingsboard.server.dao.exception.DataValidationException;
36 37 import org.thingsboard.server.dao.model.CustomerEntity;
37 38 import org.thingsboard.server.dao.model.DeviceEntity;
... ... @@ -53,7 +54,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
53 54
54 55 @Service
55 56 @Slf4j
56   -public class DeviceServiceImpl implements DeviceService {
  57 +public class DeviceServiceImpl extends BaseEntityService implements DeviceService {
57 58
58 59 @Autowired
59 60 private DeviceDao deviceDao;
... ... @@ -132,6 +133,7 @@ public class DeviceServiceImpl implements DeviceService {
132 133 if (deviceCredentials != null) {
133 134 deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
134 135 }
  136 + deleteEntityRelations(deviceId);
135 137 deviceDao.removeById(deviceId.getId());
136 138 }
137 139
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.entity;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.dao.relation.RelationService;
  22 +
  23 +/**
  24 + * Created by ashvayka on 04.05.17.
  25 + */
  26 +@Slf4j
  27 +public class BaseEntityService {
  28 +
  29 + @Autowired
  30 + protected RelationService relationService;
  31 +
  32 + protected void deleteEntityRelations(EntityId entityId) {
  33 + log.trace("Executing deleteEntityRelations [{}]", entityId);
  34 + relationService.deleteEntityRelations(entityId);
  35 + }
  36 +
  37 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.model;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.datastax.driver.mapping.annotations.*;
  20 +import com.fasterxml.jackson.databind.JsonNode;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +import org.thingsboard.server.common.data.alarm.Alarm;
  23 +import org.thingsboard.server.common.data.alarm.AlarmId;
  24 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  25 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  26 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.dao.model.type.AlarmSeverityCodec;
  29 +import org.thingsboard.server.dao.model.type.AlarmStatusCodec;
  30 +import org.thingsboard.server.dao.model.type.EntityTypeCodec;
  31 +import org.thingsboard.server.dao.model.type.JsonCodec;
  32 +
  33 +import java.util.UUID;
  34 +
  35 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  36 +
  37 +@Table(name = ALARM_COLUMN_FAMILY_NAME)
  38 +public final class AlarmEntity implements BaseEntity<Alarm> {
  39 +
  40 + @Transient
  41 + private static final long serialVersionUID = -1265181166886910152L;
  42 +
  43 + @ClusteringColumn(value = 1)
  44 + @Column(name = ID_PROPERTY)
  45 + private UUID id;
  46 +
  47 + @PartitionKey(value = 0)
  48 + @Column(name = ALARM_TENANT_ID_PROPERTY)
  49 + private UUID tenantId;
  50 +
  51 + @PartitionKey(value = 1)
  52 + @Column(name = ALARM_ORIGINATOR_ID_PROPERTY)
  53 + private UUID originatorId;
  54 +
  55 + @PartitionKey(value = 2)
  56 + @Column(name = ALARM_ORIGINATOR_TYPE_PROPERTY, codec = EntityTypeCodec.class)
  57 + private EntityType originatorType;
  58 +
  59 + @ClusteringColumn(value = 0)
  60 + @Column(name = ALARM_TYPE_PROPERTY)
  61 + private String type;
  62 +
  63 + @Column(name = ALARM_SEVERITY_PROPERTY, codec = AlarmSeverityCodec.class)
  64 + private AlarmSeverity severity;
  65 +
  66 + @Column(name = ALARM_STATUS_PROPERTY, codec = AlarmStatusCodec.class)
  67 + private AlarmStatus status;
  68 +
  69 + @Column(name = ALARM_START_TS_PROPERTY)
  70 + private Long startTs;
  71 +
  72 + @Column(name = ALARM_END_TS_PROPERTY)
  73 + private Long endTs;
  74 +
  75 + @Column(name = ALARM_ACK_TS_PROPERTY)
  76 + private Long ackTs;
  77 +
  78 + @Column(name = ALARM_CLEAR_TS_PROPERTY)
  79 + private Long clearTs;
  80 +
  81 + @Column(name = ALARM_DETAILS_PROPERTY, codec = JsonCodec.class)
  82 + private JsonNode details;
  83 +
  84 + @Column(name = ALARM_PROPAGATE_PROPERTY)
  85 + private Boolean propagate;
  86 +
  87 + public AlarmEntity() {
  88 + super();
  89 + }
  90 +
  91 + public AlarmEntity(Alarm alarm) {
  92 + if (alarm.getId() != null) {
  93 + this.id = alarm.getId().getId();
  94 + }
  95 + if (alarm.getTenantId() != null) {
  96 + this.tenantId = alarm.getTenantId().getId();
  97 + }
  98 + this.type = alarm.getType();
  99 + this.originatorId = alarm.getOriginator().getId();
  100 + this.originatorType = alarm.getOriginator().getEntityType();
  101 + this.type = alarm.getType();
  102 + this.severity = alarm.getSeverity();
  103 + this.status = alarm.getStatus();
  104 + this.propagate = alarm.isPropagate();
  105 + this.startTs = alarm.getStartTs();
  106 + this.endTs = alarm.getEndTs();
  107 + this.ackTs = alarm.getAckTs();
  108 + this.clearTs = alarm.getClearTs();
  109 + this.details = alarm.getDetails();
  110 + }
  111 +
  112 + public UUID getId() {
  113 + return id;
  114 + }
  115 +
  116 + public void setId(UUID id) {
  117 + this.id = id;
  118 + }
  119 +
  120 + public UUID getTenantId() {
  121 + return tenantId;
  122 + }
  123 +
  124 + public void setTenantId(UUID tenantId) {
  125 + this.tenantId = tenantId;
  126 + }
  127 +
  128 + @Override
  129 + public Alarm toData() {
  130 + Alarm alarm = new Alarm(new AlarmId(id));
  131 + alarm.setCreatedTime(UUIDs.unixTimestamp(id));
  132 + if (tenantId != null) {
  133 + alarm.setTenantId(new TenantId(tenantId));
  134 + }
  135 + alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
  136 + alarm.setType(type);
  137 + alarm.setSeverity(severity);
  138 + alarm.setStatus(status);
  139 + alarm.setPropagate(propagate);
  140 + alarm.setStartTs(startTs);
  141 + alarm.setEndTs(endTs);
  142 + alarm.setAckTs(ackTs);
  143 + alarm.setClearTs(clearTs);
  144 + alarm.setDetails(details);
  145 + return alarm;
  146 + }
  147 +
  148 +}
\ No newline at end of file
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.model;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.datastax.driver.mapping.annotations.Column;
  20 +import com.datastax.driver.mapping.annotations.PartitionKey;
  21 +import com.datastax.driver.mapping.annotations.Table;
  22 +import com.datastax.driver.mapping.annotations.Transient;
  23 +import com.fasterxml.jackson.databind.JsonNode;
  24 +import org.thingsboard.server.common.data.asset.Asset;
  25 +import org.thingsboard.server.common.data.id.AssetId;
  26 +import org.thingsboard.server.common.data.id.CustomerId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.dao.model.type.JsonCodec;
  29 +
  30 +import java.util.UUID;
  31 +
  32 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  33 +
  34 +@Table(name = ASSET_COLUMN_FAMILY_NAME)
  35 +public final class AssetEntity implements SearchTextEntity<Asset> {
  36 +
  37 + @Transient
  38 + private static final long serialVersionUID = -1265181166886910152L;
  39 +
  40 + @PartitionKey(value = 0)
  41 + @Column(name = ID_PROPERTY)
  42 + private UUID id;
  43 +
  44 + @PartitionKey(value = 1)
  45 + @Column(name = ASSET_TENANT_ID_PROPERTY)
  46 + private UUID tenantId;
  47 +
  48 + @PartitionKey(value = 2)
  49 + @Column(name = ASSET_CUSTOMER_ID_PROPERTY)
  50 + private UUID customerId;
  51 +
  52 + @Column(name = ASSET_NAME_PROPERTY)
  53 + private String name;
  54 +
  55 + @Column(name = ASSET_TYPE_PROPERTY)
  56 + private String type;
  57 +
  58 + @Column(name = SEARCH_TEXT_PROPERTY)
  59 + private String searchText;
  60 +
  61 + @Column(name = ASSET_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
  62 + private JsonNode additionalInfo;
  63 +
  64 + public AssetEntity() {
  65 + super();
  66 + }
  67 +
  68 + public AssetEntity(Asset asset) {
  69 + if (asset.getId() != null) {
  70 + this.id = asset.getId().getId();
  71 + }
  72 + if (asset.getTenantId() != null) {
  73 + this.tenantId = asset.getTenantId().getId();
  74 + }
  75 + if (asset.getCustomerId() != null) {
  76 + this.customerId = asset.getCustomerId().getId();
  77 + }
  78 + this.name = asset.getName();
  79 + this.type = asset.getType();
  80 + this.additionalInfo = asset.getAdditionalInfo();
  81 + }
  82 +
  83 + public UUID getId() {
  84 + return id;
  85 + }
  86 +
  87 + public void setId(UUID id) {
  88 + this.id = id;
  89 + }
  90 +
  91 + public UUID getTenantId() {
  92 + return tenantId;
  93 + }
  94 +
  95 + public void setTenantId(UUID tenantId) {
  96 + this.tenantId = tenantId;
  97 + }
  98 +
  99 + public UUID getCustomerId() {
  100 + return customerId;
  101 + }
  102 +
  103 + public void setCustomerId(UUID customerId) {
  104 + this.customerId = customerId;
  105 + }
  106 +
  107 + public String getName() {
  108 + return name;
  109 + }
  110 +
  111 + public void setName(String name) {
  112 + this.name = name;
  113 + }
  114 +
  115 + public String getType() {
  116 + return type;
  117 + }
  118 +
  119 + public void setType(String type) {
  120 + this.type = type;
  121 + }
  122 +
  123 + public JsonNode getAdditionalInfo() {
  124 + return additionalInfo;
  125 + }
  126 +
  127 + public void setAdditionalInfo(JsonNode additionalInfo) {
  128 + this.additionalInfo = additionalInfo;
  129 + }
  130 +
  131 + @Override
  132 + public String getSearchTextSource() {
  133 + return name;
  134 + }
  135 +
  136 + @Override
  137 + public void setSearchText(String searchText) {
  138 + this.searchText = searchText;
  139 + }
  140 +
  141 + public String getSearchText() {
  142 + return searchText;
  143 + }
  144 +
  145 + @Override
  146 + public int hashCode() {
  147 + final int prime = 31;
  148 + int result = 1;
  149 + result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode());
  150 + result = prime * result + ((customerId == null) ? 0 : customerId.hashCode());
  151 + result = prime * result + ((id == null) ? 0 : id.hashCode());
  152 + result = prime * result + ((name == null) ? 0 : name.hashCode());
  153 + result = prime * result + ((type == null) ? 0 : type.hashCode());
  154 + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode());
  155 + return result;
  156 + }
  157 +
  158 + @Override
  159 + public boolean equals(Object obj) {
  160 + if (this == obj)
  161 + return true;
  162 + if (obj == null)
  163 + return false;
  164 + if (getClass() != obj.getClass())
  165 + return false;
  166 + AssetEntity other = (AssetEntity) obj;
  167 + if (additionalInfo == null) {
  168 + if (other.additionalInfo != null)
  169 + return false;
  170 + } else if (!additionalInfo.equals(other.additionalInfo))
  171 + return false;
  172 + if (customerId == null) {
  173 + if (other.customerId != null)
  174 + return false;
  175 + } else if (!customerId.equals(other.customerId))
  176 + return false;
  177 + if (id == null) {
  178 + if (other.id != null)
  179 + return false;
  180 + } else if (!id.equals(other.id))
  181 + return false;
  182 + if (name == null) {
  183 + if (other.name != null)
  184 + return false;
  185 + } else if (!name.equals(other.name))
  186 + return false;
  187 + if (type == null) {
  188 + if (other.type != null)
  189 + return false;
  190 + } else if (!type.equals(other.type))
  191 + return false;
  192 + if (tenantId == null) {
  193 + if (other.tenantId != null)
  194 + return false;
  195 + } else if (!tenantId.equals(other.tenantId))
  196 + return false;
  197 + return true;
  198 + }
  199 +
  200 + @Override
  201 + public String toString() {
  202 + StringBuilder builder = new StringBuilder();
  203 + builder.append("AssetEntity [id=");
  204 + builder.append(id);
  205 + builder.append(", tenantId=");
  206 + builder.append(tenantId);
  207 + builder.append(", customerId=");
  208 + builder.append(customerId);
  209 + builder.append(", name=");
  210 + builder.append(name);
  211 + builder.append(", type=");
  212 + builder.append(type);
  213 + builder.append(", additionalInfo=");
  214 + builder.append(additionalInfo);
  215 + builder.append("]");
  216 + return builder.toString();
  217 + }
  218 +
  219 + @Override
  220 + public Asset toData() {
  221 + Asset asset = new Asset(new AssetId(id));
  222 + asset.setCreatedTime(UUIDs.unixTimestamp(id));
  223 + if (tenantId != null) {
  224 + asset.setTenantId(new TenantId(tenantId));
  225 + }
  226 + if (customerId != null) {
  227 + asset.setCustomerId(new CustomerId(customerId));
  228 + }
  229 + asset.setName(name);
  230 + asset.setType(type);
  231 + asset.setAdditionalInfo(additionalInfo);
  232 + return asset;
  233 + }
  234 +
  235 +}
\ No newline at end of file
... ...
... ... @@ -35,7 +35,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
35 35 */
36 36 @Data
37 37 @NoArgsConstructor
38   -@Table(name = DEVICE_COLUMN_FAMILY_NAME)
  38 +@Table(name = EVENT_COLUMN_FAMILY_NAME)
39 39 public class EventEntity implements BaseEntity<Event> {
40 40
41 41 @Transient
... ... @@ -98,23 +98,7 @@ public class EventEntity implements BaseEntity<Event> {
98 98 Event event = new Event(new EventId(id));
99 99 event.setCreatedTime(UUIDs.unixTimestamp(id));
100 100 event.setTenantId(new TenantId(tenantId));
101   - switch (entityType) {
102   - case TENANT:
103   - event.setEntityId(new TenantId(entityId));
104   - break;
105   - case DEVICE:
106   - event.setEntityId(new DeviceId(entityId));
107   - break;
108   - case CUSTOMER:
109   - event.setEntityId(new CustomerId(entityId));
110   - break;
111   - case RULE:
112   - event.setEntityId(new RuleId(entityId));
113   - break;
114   - case PLUGIN:
115   - event.setEntityId(new PluginId(entityId));
116   - break;
117   - }
  101 + event.setEntityId(EntityIdFactory.getByTypeAndUuid(entityType, entityId));
118 102 event.setBody(body);
119 103 event.setType(eventType);
120 104 event.setUid(eventUId);
... ...
... ... @@ -126,6 +126,49 @@ public class ModelConstants {
126 126 public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
127 127 public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
128 128
  129 + /**
  130 + * Cassandra asset constants.
  131 + */
  132 + public static final String ASSET_COLUMN_FAMILY_NAME = "asset";
  133 + public static final String ASSET_TENANT_ID_PROPERTY = TENTANT_ID_PROPERTY;
  134 + public static final String ASSET_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
  135 + public static final String ASSET_NAME_PROPERTY = "name";
  136 + public static final String ASSET_TYPE_PROPERTY = "type";
  137 + public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
  138 +
  139 + public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
  140 + public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
  141 + public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
  142 +
  143 + /**
  144 + * Cassandra alarm constants.
  145 + */
  146 + public static final String ALARM_COLUMN_FAMILY_NAME = "alarm";
  147 + public static final String ALARM_TENANT_ID_PROPERTY = TENTANT_ID_PROPERTY;
  148 + public static final String ALARM_TYPE_PROPERTY = "type";
  149 + public static final String ALARM_DETAILS_PROPERTY = "details";
  150 + public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";
  151 + public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type";
  152 + public static final String ALARM_SEVERITY_PROPERTY = "severity";
  153 + public static final String ALARM_STATUS_PROPERTY = "status";
  154 + public static final String ALARM_START_TS_PROPERTY = "start_ts";
  155 + public static final String ALARM_END_TS_PROPERTY = "end_ts";
  156 + public static final String ALARM_ACK_TS_PROPERTY = "ack_ts";
  157 + public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts";
  158 + public static final String ALARM_PROPAGATE_PROPERTY = "propagate";
  159 +
  160 + /**
  161 + * Cassandra entity relation constants.
  162 + */
  163 + public static final String RELATION_COLUMN_FAMILY_NAME = "relation";
  164 + public static final String RELATION_FROM_ID_PROPERTY = "from_id";
  165 + public static final String RELATION_FROM_TYPE_PROPERTY = "from_type";
  166 + public static final String RELATION_TO_ID_PROPERTY = "to_id";
  167 + public static final String RELATION_TO_TYPE_PROPERTY = "to_type";
  168 + public static final String RELATION_TYPE_PROPERTY = "relation_type";
  169 +
  170 + public static final String RELATION_REVERSE_VIEW_NAME = "reverse_relation";
  171 +
129 172
130 173 /**
131 174 * Cassandra device_credentials constants.
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  20 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  21 +import org.thingsboard.server.dao.alarm.AlarmService;
  22 +
  23 +public class AlarmSeverityCodec extends EnumNameCodec<AlarmSeverity> {
  24 +
  25 + public AlarmSeverityCodec() {
  26 + super(AlarmSeverity.class);
  27 + }
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.model.type;
  17 +
  18 +import com.datastax.driver.extras.codecs.enums.EnumNameCodec;
  19 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  20 +
  21 +public class AlarmStatusCodec extends EnumNameCodec<AlarmStatus> {
  22 +
  23 + public AlarmStatusCodec() {
  24 + super(AlarmStatus.class);
  25 + }
  26 +
  27 +}
... ...
... ... @@ -15,13 +15,14 @@
15 15 */
16 16 package org.thingsboard.server.dao.plugin;
17 17
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
18 20 import lombok.extern.slf4j.Slf4j;
19 21 import org.apache.commons.lang3.StringUtils;
20   -import org.slf4j.Logger;
21   -import org.slf4j.LoggerFactory;
22 22 import org.springframework.beans.factory.annotation.Autowired;
23 23 import org.springframework.stereotype.Service;
24 24 import org.thingsboard.server.common.data.id.PluginId;
  25 +import org.thingsboard.server.common.data.id.RuleId;
25 26 import org.thingsboard.server.common.data.id.TenantId;
26 27 import org.thingsboard.server.common.data.page.TextPageData;
27 28 import org.thingsboard.server.common.data.page.TextPageLink;
... ... @@ -29,7 +30,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
29 30 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
30 31 import org.thingsboard.server.common.data.plugin.ComponentType;
31 32 import org.thingsboard.server.common.data.plugin.PluginMetaData;
  33 +import org.thingsboard.server.common.data.rule.RuleMetaData;
32 34 import org.thingsboard.server.dao.component.ComponentDescriptorService;
  35 +import org.thingsboard.server.dao.entity.BaseEntityService;
33 36 import org.thingsboard.server.dao.exception.DataValidationException;
34 37 import org.thingsboard.server.dao.exception.DatabaseException;
35 38 import org.thingsboard.server.dao.exception.IncorrectParameterException;
... ... @@ -48,10 +51,11 @@ import java.util.stream.Collectors;
48 51
49 52 import static org.thingsboard.server.dao.DaoUtil.convertDataList;
50 53 import static org.thingsboard.server.dao.DaoUtil.getData;
  54 +import static org.thingsboard.server.dao.service.Validator.validateId;
51 55
52 56 @Service
53 57 @Slf4j
54   -public class BasePluginService implements PluginService {
  58 +public class BasePluginService extends BaseEntityService implements PluginService {
55 59
56 60 //TODO: move to a better place.
57 61 public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
... ... @@ -109,6 +113,13 @@ public class BasePluginService implements PluginService {
109 113 }
110 114
111 115 @Override
  116 + public ListenableFuture<PluginMetaData> findPluginByIdAsync(PluginId pluginId) {
  117 + validateId(pluginId, "Incorrect plugin id for search plugin request.");
  118 + ListenableFuture<PluginMetaDataEntity> pluginEntity = pluginDao.findByIdAsync(pluginId.getId());
  119 + return Futures.transform(pluginEntity, (com.google.common.base.Function<? super PluginMetaDataEntity, ? extends PluginMetaData>) input -> getData(input));
  120 + }
  121 +
  122 + @Override
112 123 public PluginMetaData findPluginByApiToken(String apiToken) {
113 124 Validator.validateString(apiToken, "Incorrect plugin apiToken for search request.");
114 125 return getData(pluginDao.findByApiToken(apiToken));
... ... @@ -205,6 +216,7 @@ public class BasePluginService implements PluginService {
205 216 @Override
206 217 public void deletePluginById(PluginId pluginId) {
207 218 Validator.validateId(pluginId, "Incorrect plugin id for delete request.");
  219 + deleteEntityRelations(pluginId);
208 220 checkRulesAndDelete(pluginId.getId());
209 221 }
210 222
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.plugin;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.id.PluginId;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -29,6 +30,8 @@ public interface PluginService {
29 30
30 31 PluginMetaData findPluginById(PluginId pluginId);
31 32
  33 + ListenableFuture<PluginMetaData> findPluginByIdAsync(PluginId pluginId);
  34 +
32 35 PluginMetaData findPluginByApiToken(String apiToken);
33 36
34 37 TextPageData<PluginMetaData> findSystemPlugins(TextPageLink pageLink);
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import com.datastax.driver.core.*;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.google.common.base.Function;
  21 +import com.google.common.util.concurrent.Futures;
  22 +import com.google.common.util.concurrent.ListenableFuture;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.data.id.EntityId;
  26 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  27 +import org.thingsboard.server.common.data.relation.EntityRelation;
  28 +import org.thingsboard.server.dao.AbstractAsyncDao;
  29 +import org.thingsboard.server.dao.model.ModelConstants;
  30 +
  31 +import javax.annotation.Nullable;
  32 +import javax.annotation.PostConstruct;
  33 +import java.util.ArrayList;
  34 +import java.util.List;
  35 +
  36 +/**
  37 + * Created by ashvayka on 25.04.17.
  38 + */
  39 +@Component
  40 +@Slf4j
  41 +public class BaseRelationDao extends AbstractAsyncDao implements RelationDao {
  42 +
  43 + private static final String SELECT_COLUMNS = "SELECT " +
  44 + ModelConstants.RELATION_FROM_ID_PROPERTY + "," +
  45 + ModelConstants.RELATION_FROM_TYPE_PROPERTY + "," +
  46 + ModelConstants.RELATION_TO_ID_PROPERTY + "," +
  47 + ModelConstants.RELATION_TO_TYPE_PROPERTY + "," +
  48 + ModelConstants.RELATION_TYPE_PROPERTY + "," +
  49 + ModelConstants.ADDITIONAL_INFO_PROPERTY;
  50 + public static final String FROM = " FROM ";
  51 + public static final String WHERE = " WHERE ";
  52 + public static final String AND = " AND ";
  53 +
  54 + private PreparedStatement saveStmt;
  55 + private PreparedStatement findAllByFromStmt;
  56 + private PreparedStatement findAllByFromAndTypeStmt;
  57 + private PreparedStatement findAllByToStmt;
  58 + private PreparedStatement findAllByToAndTypeStmt;
  59 + private PreparedStatement checkRelationStmt;
  60 + private PreparedStatement deleteStmt;
  61 + private PreparedStatement deleteAllByEntityStmt;
  62 +
  63 + @PostConstruct
  64 + public void init() {
  65 + super.startExecutor();
  66 + }
  67 +
  68 + @Override
  69 + public ListenableFuture<List<EntityRelation>> findAllByFrom(EntityId from) {
  70 + BoundStatement stmt = getFindAllByFromStmt().bind().setUUID(0, from.getId()).setString(1, from.getEntityType().name());
  71 + return executeAsyncRead(from, stmt);
  72 + }
  73 +
  74 + @Override
  75 + public ListenableFuture<List<EntityRelation>> findAllByFromAndType(EntityId from, String relationType) {
  76 + BoundStatement stmt = getFindAllByFromAndTypeStmt().bind()
  77 + .setUUID(0, from.getId())
  78 + .setString(1, from.getEntityType().name())
  79 + .setString(2, relationType);
  80 + return executeAsyncRead(from, stmt);
  81 + }
  82 +
  83 + @Override
  84 + public ListenableFuture<List<EntityRelation>> findAllByTo(EntityId to) {
  85 + BoundStatement stmt = getFindAllByToStmt().bind().setUUID(0, to.getId()).setString(1, to.getEntityType().name());
  86 + return executeAsyncRead(to, stmt);
  87 + }
  88 +
  89 + @Override
  90 + public ListenableFuture<List<EntityRelation>> findAllByToAndType(EntityId to, String relationType) {
  91 + BoundStatement stmt = getFindAllByToAndTypeStmt().bind()
  92 + .setUUID(0, to.getId())
  93 + .setString(1, to.getEntityType().name())
  94 + .setString(2, relationType);
  95 + return executeAsyncRead(to, stmt);
  96 + }
  97 +
  98 + @Override
  99 + public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType) {
  100 + BoundStatement stmt = getCheckRelationStmt().bind()
  101 + .setUUID(0, from.getId())
  102 + .setString(1, from.getEntityType().name())
  103 + .setUUID(2, to.getId())
  104 + .setString(3, to.getEntityType().name())
  105 + .setString(4, relationType);
  106 + return getFuture(executeAsyncRead(stmt), rs -> rs != null ? rs.one() != null : false);
  107 + }
  108 +
  109 + @Override
  110 + public ListenableFuture<Boolean> saveRelation(EntityRelation relation) {
  111 + BoundStatement stmt = getSaveStmt().bind()
  112 + .setUUID(0, relation.getFrom().getId())
  113 + .setString(1, relation.getFrom().getEntityType().name())
  114 + .setUUID(2, relation.getTo().getId())
  115 + .setString(3, relation.getTo().getEntityType().name())
  116 + .setString(4, relation.getType())
  117 + .set(5, relation.getAdditionalInfo(), JsonNode.class);
  118 + ResultSetFuture future = executeAsyncWrite(stmt);
  119 + return getBooleanListenableFuture(future);
  120 + }
  121 +
  122 + @Override
  123 + public ListenableFuture<Boolean> deleteRelation(EntityRelation relation) {
  124 + return deleteRelation(relation.getFrom(), relation.getTo(), relation.getType());
  125 + }
  126 +
  127 + @Override
  128 + public ListenableFuture<Boolean> deleteRelation(EntityId from, EntityId to, String relationType) {
  129 + BoundStatement stmt = getDeleteStmt().bind()
  130 + .setUUID(0, from.getId())
  131 + .setString(1, from.getEntityType().name())
  132 + .setUUID(2, to.getId())
  133 + .setString(3, to.getEntityType().name())
  134 + .setString(4, relationType);
  135 + ResultSetFuture future = executeAsyncWrite(stmt);
  136 + return getBooleanListenableFuture(future);
  137 + }
  138 +
  139 + @Override
  140 + public ListenableFuture<Boolean> deleteOutboundRelations(EntityId entity) {
  141 + BoundStatement stmt = getDeleteAllByEntityStmt().bind()
  142 + .setUUID(0, entity.getId())
  143 + .setString(1, entity.getEntityType().name());
  144 + ResultSetFuture future = executeAsyncWrite(stmt);
  145 + return getBooleanListenableFuture(future);
  146 + }
  147 +
  148 + private PreparedStatement getSaveStmt() {
  149 + if (saveStmt == null) {
  150 + saveStmt = getSession().prepare("INSERT INTO " + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
  151 + "(" + ModelConstants.RELATION_FROM_ID_PROPERTY +
  152 + "," + ModelConstants.RELATION_FROM_TYPE_PROPERTY +
  153 + "," + ModelConstants.RELATION_TO_ID_PROPERTY +
  154 + "," + ModelConstants.RELATION_TO_TYPE_PROPERTY +
  155 + "," + ModelConstants.RELATION_TYPE_PROPERTY +
  156 + "," + ModelConstants.ADDITIONAL_INFO_PROPERTY + ")" +
  157 + " VALUES(?, ?, ?, ?, ?, ?)");
  158 + }
  159 + return saveStmt;
  160 + }
  161 +
  162 + private PreparedStatement getDeleteStmt() {
  163 + if (deleteStmt == null) {
  164 + deleteStmt = getSession().prepare("DELETE FROM " + ModelConstants.RELATION_COLUMN_FAMILY_NAME +
  165 + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ?" +
  166 + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ?" +
  167 + AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ?" +
  168 + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ?" +
  169 + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ?");
  170 + }
  171 + return deleteStmt;
  172 + }
  173 +
  174 + private PreparedStatement getDeleteAllByEntityStmt() {
  175 + if (deleteAllByEntityStmt == null) {
  176 + deleteAllByEntityStmt = getSession().prepare("DELETE FROM " + ModelConstants.RELATION_COLUMN_FAMILY_NAME +
  177 + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ?" +
  178 + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ?");
  179 + }
  180 + return deleteAllByEntityStmt;
  181 + }
  182 +
  183 + private PreparedStatement getFindAllByFromStmt() {
  184 + if (findAllByFromStmt == null) {
  185 + findAllByFromStmt = getSession().prepare(SELECT_COLUMNS + " " +
  186 + FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
  187 + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
  188 + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? ");
  189 + }
  190 + return findAllByFromStmt;
  191 + }
  192 +
  193 + private PreparedStatement getFindAllByFromAndTypeStmt() {
  194 + if (findAllByFromAndTypeStmt == null) {
  195 + findAllByFromAndTypeStmt = getSession().prepare(SELECT_COLUMNS + " " +
  196 + FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
  197 + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
  198 + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " +
  199 + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
  200 + }
  201 + return findAllByFromAndTypeStmt;
  202 + }
  203 +
  204 + private PreparedStatement getFindAllByToStmt() {
  205 + if (findAllByToStmt == null) {
  206 + findAllByToStmt = getSession().prepare(SELECT_COLUMNS + " " +
  207 + FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " +
  208 + WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
  209 + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? ");
  210 + }
  211 + return findAllByToStmt;
  212 + }
  213 +
  214 + private PreparedStatement getFindAllByToAndTypeStmt() {
  215 + if (findAllByToAndTypeStmt == null) {
  216 + findAllByToAndTypeStmt = getSession().prepare(SELECT_COLUMNS + " " +
  217 + FROM + ModelConstants.RELATION_REVERSE_VIEW_NAME + " " +
  218 + WHERE + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
  219 + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " +
  220 + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
  221 + }
  222 + return findAllByToAndTypeStmt;
  223 + }
  224 +
  225 + private PreparedStatement getCheckRelationStmt() {
  226 + if (checkRelationStmt == null) {
  227 + checkRelationStmt = getSession().prepare(SELECT_COLUMNS + " " +
  228 + FROM + ModelConstants.RELATION_COLUMN_FAMILY_NAME + " " +
  229 + WHERE + ModelConstants.RELATION_FROM_ID_PROPERTY + " = ? " +
  230 + AND + ModelConstants.RELATION_FROM_TYPE_PROPERTY + " = ? " +
  231 + AND + ModelConstants.RELATION_TO_ID_PROPERTY + " = ? " +
  232 + AND + ModelConstants.RELATION_TO_TYPE_PROPERTY + " = ? " +
  233 + AND + ModelConstants.RELATION_TYPE_PROPERTY + " = ? ");
  234 + }
  235 + return checkRelationStmt;
  236 + }
  237 +
  238 + private EntityRelation getEntityRelation(Row row) {
  239 + EntityRelation relation = new EntityRelation();
  240 + relation.setType(row.getString(ModelConstants.RELATION_TYPE_PROPERTY));
  241 + relation.setAdditionalInfo(row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY, JsonNode.class));
  242 + relation.setFrom(toEntity(row, ModelConstants.RELATION_FROM_ID_PROPERTY, ModelConstants.RELATION_FROM_TYPE_PROPERTY));
  243 + relation.setTo(toEntity(row, ModelConstants.RELATION_TO_ID_PROPERTY, ModelConstants.RELATION_TO_TYPE_PROPERTY));
  244 + return relation;
  245 + }
  246 +
  247 + private EntityId toEntity(Row row, String uuidColumn, String typeColumn) {
  248 + return EntityIdFactory.getByTypeAndUuid(row.getString(typeColumn), row.getUUID(uuidColumn));
  249 + }
  250 +
  251 + private ListenableFuture<List<EntityRelation>> executeAsyncRead(EntityId from, BoundStatement stmt) {
  252 + log.debug("Generated query [{}] for entity {}", stmt, from);
  253 + return getFuture(executeAsyncRead(stmt), rs -> {
  254 + List<Row> rows = rs.all();
  255 + List<EntityRelation> entries = new ArrayList<>(rows.size());
  256 + if (!rows.isEmpty()) {
  257 + rows.forEach(row -> {
  258 + entries.add(getEntityRelation(row));
  259 + });
  260 + }
  261 + return entries;
  262 + });
  263 + }
  264 +
  265 + private ListenableFuture<Boolean> getBooleanListenableFuture(ResultSetFuture rsFuture) {
  266 + return getFuture(rsFuture, rs -> rs != null ? rs.wasApplied() : false);
  267 + }
  268 +
  269 + private <T> ListenableFuture<T> getFuture(ResultSetFuture future, java.util.function.Function<ResultSet, T> transformer) {
  270 + return Futures.transform(future, new Function<ResultSet, T>() {
  271 + @Nullable
  272 + @Override
  273 + public T apply(@Nullable ResultSet input) {
  274 + return transformer.apply(input);
  275 + }
  276 + }, readResultsProcessingExecutor);
  277 + }
  278 +
  279 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import com.google.common.base.Function;
  19 +import com.google.common.util.concurrent.AsyncFunction;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.springframework.stereotype.Service;
  25 +import org.springframework.util.StringUtils;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.relation.EntityRelation;
  28 +import org.thingsboard.server.dao.exception.DataValidationException;
  29 +
  30 +import javax.annotation.Nullable;
  31 +import java.util.*;
  32 +import java.util.concurrent.ConcurrentHashMap;
  33 +
  34 +/**
  35 + * Created by ashvayka on 28.04.17.
  36 + */
  37 +@Service
  38 +@Slf4j
  39 +public class BaseRelationService implements RelationService {
  40 +
  41 + @Autowired
  42 + private RelationDao relationDao;
  43 +
  44 + @Override
  45 + public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType) {
  46 + log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType);
  47 + validate(from, to, relationType);
  48 + return relationDao.checkRelation(from, to, relationType);
  49 + }
  50 +
  51 + @Override
  52 + public ListenableFuture<Boolean> saveRelation(EntityRelation relation) {
  53 + log.trace("Executing saveRelation [{}]", relation);
  54 + validate(relation);
  55 + return relationDao.saveRelation(relation);
  56 + }
  57 +
  58 + @Override
  59 + public ListenableFuture<Boolean> deleteRelation(EntityRelation relation) {
  60 + log.trace("Executing deleteRelation [{}]", relation);
  61 + validate(relation);
  62 + return relationDao.deleteRelation(relation);
  63 + }
  64 +
  65 + @Override
  66 + public ListenableFuture<Boolean> deleteRelation(EntityId from, EntityId to, String relationType) {
  67 + log.trace("Executing deleteRelation [{}][{}][{}]", from, to, relationType);
  68 + validate(from, to, relationType);
  69 + return relationDao.deleteRelation(from, to, relationType);
  70 + }
  71 +
  72 + @Override
  73 + public ListenableFuture<Boolean> deleteEntityRelations(EntityId entity) {
  74 + log.trace("Executing deleteEntityRelations [{}]", entity);
  75 + validate(entity);
  76 + ListenableFuture<List<EntityRelation>> inboundRelations = relationDao.findAllByTo(entity);
  77 + ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, new AsyncFunction<List<EntityRelation>, List<Boolean>>() {
  78 + @Override
  79 + public ListenableFuture<List<Boolean>> apply(List<EntityRelation> relations) throws Exception {
  80 + List<ListenableFuture<Boolean>> results = new ArrayList<>();
  81 + for (EntityRelation relation : relations) {
  82 + results.add(relationDao.deleteRelation(relation));
  83 + }
  84 + return Futures.allAsList(results);
  85 + }
  86 + });
  87 +
  88 + ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction());
  89 +
  90 + ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelations(entity);
  91 +
  92 + return Futures.transform(Futures.allAsList(Arrays.asList(inboundFuture, outboundFuture)), getListToBooleanFunction());
  93 + }
  94 +
  95 + @Override
  96 + public ListenableFuture<List<EntityRelation>> findByFrom(EntityId from) {
  97 + log.trace("Executing findByFrom [{}]", from);
  98 + validate(from);
  99 + return relationDao.findAllByFrom(from);
  100 + }
  101 +
  102 + @Override
  103 + public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {
  104 + log.trace("Executing findByFromAndType [{}][{}]", from, relationType);
  105 + validate(from);
  106 + validateType(relationType);
  107 + return relationDao.findAllByFromAndType(from, relationType);
  108 + }
  109 +
  110 + @Override
  111 + public ListenableFuture<List<EntityRelation>> findByTo(EntityId to) {
  112 + log.trace("Executing findByTo [{}]", to);
  113 + validate(to);
  114 + return relationDao.findAllByTo(to);
  115 + }
  116 +
  117 + @Override
  118 + public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType) {
  119 + log.trace("Executing findByToAndType [{}][{}]", to, relationType);
  120 + validate(to);
  121 + validateType(relationType);
  122 + return relationDao.findAllByToAndType(to, relationType);
  123 + }
  124 +
  125 + @Override
  126 + public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) {
  127 + log.trace("Executing findByQuery [{}][{}]", query);
  128 + RelationsSearchParameters params = query.getParameters();
  129 + final List<EntityTypeFilter> filters = query.getFilters();
  130 + if (filters == null || filters.isEmpty()) {
  131 + log.debug("Filters are not set [{}]", query);
  132 + }
  133 +
  134 + int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE;
  135 +
  136 + try {
  137 + ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), maxLvl, new ConcurrentHashMap<>());
  138 + return Futures.transform(relationSet, (Function<Set<EntityRelation>, List<EntityRelation>>) input -> {
  139 + List<EntityRelation> relations = new ArrayList<>();
  140 + for (EntityRelation relation : input) {
  141 + if (filters == null || filters.isEmpty()) {
  142 + relations.add(relation);
  143 + } else {
  144 + for (EntityTypeFilter filter : filters) {
  145 + if (match(filter, relation, params.getDirection())) {
  146 + relations.add(relation);
  147 + break;
  148 + }
  149 + }
  150 + }
  151 + }
  152 + return relations;
  153 + });
  154 + } catch (Exception e) {
  155 + log.warn("Failed to query relations: [{}]", query, e);
  156 + throw new RuntimeException(e);
  157 + }
  158 + }
  159 +
  160 + protected void validate(EntityRelation relation) {
  161 + if (relation == null) {
  162 + throw new DataValidationException("Relation type should be specified!");
  163 + }
  164 + validate(relation.getFrom(), relation.getTo(), relation.getType());
  165 + }
  166 +
  167 + protected void validate(EntityId from, EntityId to, String type) {
  168 + validateType(type);
  169 + if (from == null) {
  170 + throw new DataValidationException("Relation should contain from entity!");
  171 + }
  172 + if (to == null) {
  173 + throw new DataValidationException("Relation should contain to entity!");
  174 + }
  175 + }
  176 +
  177 + private void validateType(String type) {
  178 + if (StringUtils.isEmpty(type)) {
  179 + throw new DataValidationException("Relation type should be specified!");
  180 + }
  181 + }
  182 +
  183 + protected void validate(EntityId entity) {
  184 + if (entity == null) {
  185 + throw new DataValidationException("Entity should be specified!");
  186 + }
  187 + }
  188 +
  189 + private Function<List<Boolean>, Boolean> getListToBooleanFunction() {
  190 + return new Function<List<Boolean>, Boolean>() {
  191 + @Nullable
  192 + @Override
  193 + public Boolean apply(@Nullable List<Boolean> results) {
  194 + for (Boolean result : results) {
  195 + if (result == null || !result) {
  196 + return false;
  197 + }
  198 + }
  199 + return true;
  200 + }
  201 + };
  202 + }
  203 +
  204 + private boolean match(EntityTypeFilter filter, EntityRelation relation, EntitySearchDirection direction) {
  205 + if (StringUtils.isEmpty(filter.getRelationType()) || filter.getRelationType().equals(relation.getType())) {
  206 + if (filter.getEntityTypes() == null || filter.getEntityTypes().isEmpty()) {
  207 + return true;
  208 + } else {
  209 + EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
  210 + return filter.getEntityTypes().contains(entityId.getEntityType());
  211 + }
  212 + } else {
  213 + return false;
  214 + }
  215 + }
  216 +
  217 + private ListenableFuture<Set<EntityRelation>> findRelationsRecursively(final EntityId rootId, final EntitySearchDirection direction, int lvl,
  218 + final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception {
  219 + if (lvl == 0) {
  220 + return Futures.immediateFuture(Collections.emptySet());
  221 + }
  222 + lvl--;
  223 + //TODO: try to remove this blocking operation
  224 + Set<EntityRelation> children = new HashSet<>(findRelations(rootId, direction).get());
  225 + Set<EntityId> childrenIds = new HashSet<>();
  226 + for (EntityRelation childRelation : children) {
  227 + log.info("Found Relation: {}", childRelation);
  228 + EntityId childId;
  229 + if (direction == EntitySearchDirection.FROM) {
  230 + childId = childRelation.getTo();
  231 + } else {
  232 + childId = childRelation.getFrom();
  233 + }
  234 + if (uniqueMap.putIfAbsent(childId, Boolean.TRUE) == null) {
  235 + log.info("Adding Relation: {}", childId);
  236 + if (childrenIds.add(childId)) {
  237 + log.info("Added Relation: {}", childId);
  238 + }
  239 + }
  240 + }
  241 + List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>();
  242 + for (EntityId entityId : childrenIds) {
  243 + futures.add(findRelationsRecursively(entityId, direction, lvl, uniqueMap));
  244 + }
  245 + //TODO: try to remove this blocking operation
  246 + List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
  247 + relations.forEach(r -> r.forEach(d -> children.add(d)));
  248 + return Futures.immediateFuture(children);
  249 + }
  250 +
  251 + private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction) {
  252 + ListenableFuture<List<EntityRelation>> relations;
  253 + if (direction == EntitySearchDirection.FROM) {
  254 + relations = findByFrom(rootId);
  255 + } else {
  256 + relations = findByTo(rootId);
  257 + }
  258 + return relations;
  259 + }
  260 +
  261 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +/**
  23 + * Created by ashvayka on 02.05.17.
  24 + */
  25 +@Data
  26 +public class EntityRelationsQuery {
  27 +
  28 + private RelationsSearchParameters parameters;
  29 + private List<EntityTypeFilter> filters;
  30 +
  31 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +/**
  19 + * Created by ashvayka on 02.05.17.
  20 + */
  21 +public enum EntitySearchDirection {
  22 +
  23 + FROM, TO;
  24 +
  25 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.EntityType;
  21 +
  22 +import javax.annotation.Nullable;
  23 +import java.util.List;
  24 +
  25 +/**
  26 + * Created by ashvayka on 02.05.17.
  27 + */
  28 +@Data
  29 +@AllArgsConstructor
  30 +public class EntityTypeFilter {
  31 + @Nullable
  32 + private String relationType;
  33 + @Nullable
  34 + private List<EntityType> entityTypes;
  35 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.relation.EntityRelation;
  21 +
  22 +import java.util.List;
  23 +
  24 +/**
  25 + * Created by ashvayka on 25.04.17.
  26 + */
  27 +public interface RelationDao {
  28 +
  29 + ListenableFuture<List<EntityRelation>> findAllByFrom(EntityId from);
  30 +
  31 + ListenableFuture<List<EntityRelation>> findAllByFromAndType(EntityId from, String relationType);
  32 +
  33 + ListenableFuture<List<EntityRelation>> findAllByTo(EntityId to);
  34 +
  35 + ListenableFuture<List<EntityRelation>> findAllByToAndType(EntityId to, String relationType);
  36 +
  37 + ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType);
  38 +
  39 + ListenableFuture<Boolean> saveRelation(EntityRelation relation);
  40 +
  41 + ListenableFuture<Boolean> deleteRelation(EntityRelation relation);
  42 +
  43 + ListenableFuture<Boolean> deleteRelation(EntityId from, EntityId to, String relationType);
  44 +
  45 + ListenableFuture<Boolean> deleteOutboundRelations(EntityId entity);
  46 +
  47 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.relation.EntityRelation;
  21 +
  22 +import java.util.List;
  23 +
  24 +/**
  25 + * Created by ashvayka on 27.04.17.
  26 + */
  27 +public interface RelationService {
  28 +
  29 + ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType);
  30 +
  31 + ListenableFuture<Boolean> saveRelation(EntityRelation relation);
  32 +
  33 + ListenableFuture<Boolean> deleteRelation(EntityRelation relation);
  34 +
  35 + ListenableFuture<Boolean> deleteRelation(EntityId from, EntityId to, String relationType);
  36 +
  37 + ListenableFuture<Boolean> deleteEntityRelations(EntityId entity);
  38 +
  39 + ListenableFuture<List<EntityRelation>> findByFrom(EntityId from);
  40 +
  41 + ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType);
  42 +
  43 + ListenableFuture<List<EntityRelation>> findByTo(EntityId to);
  44 +
  45 + ListenableFuture<List<EntityRelation>> findByToAndType(EntityId to, String relationType);
  46 +
  47 + ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query);
  48 +
  49 +// TODO: This method may be useful for some validations in the future
  50 +// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
  51 +
  52 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.relation;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.EntityType;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  23 +
  24 +import java.util.UUID;
  25 +
  26 +/**
  27 + * Created by ashvayka on 03.05.17.
  28 + */
  29 +@Data
  30 +@AllArgsConstructor
  31 +public class RelationsSearchParameters {
  32 +
  33 + private UUID rootId;
  34 + private EntityType rootType;
  35 + private EntitySearchDirection direction;
  36 + private int maxLevel = 1;
  37 +
  38 + public RelationsSearchParameters(EntityId entityId, EntitySearchDirection direction, int maxLevel) {
  39 + this.rootId = entityId.getId();
  40 + this.rootType = entityId.getEntityType();
  41 + this.direction = direction;
  42 + this.maxLevel = maxLevel;
  43 + }
  44 +
  45 + public EntityId getEntityId() {
  46 + return EntityIdFactory.getByTypeAndUuid(rootType, rootId);
  47 + }
  48 +}
... ...
... ... @@ -17,10 +17,13 @@ package org.thingsboard.server.dao.rule;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19 19 import com.fasterxml.jackson.databind.node.ArrayNode;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
20 22 import lombok.extern.slf4j.Slf4j;
21 23 import org.apache.commons.lang3.StringUtils;
22 24 import org.springframework.beans.factory.annotation.Autowired;
23 25 import org.springframework.stereotype.Service;
  26 +import org.thingsboard.server.common.data.asset.Asset;
24 27 import org.thingsboard.server.common.data.id.RuleId;
25 28 import org.thingsboard.server.common.data.id.TenantId;
26 29 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -31,9 +34,11 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
31 34 import org.thingsboard.server.common.data.plugin.PluginMetaData;
32 35 import org.thingsboard.server.common.data.rule.RuleMetaData;
33 36 import org.thingsboard.server.dao.component.ComponentDescriptorService;
  37 +import org.thingsboard.server.dao.entity.BaseEntityService;
34 38 import org.thingsboard.server.dao.exception.DataValidationException;
35 39 import org.thingsboard.server.dao.exception.DatabaseException;
36 40 import org.thingsboard.server.dao.exception.IncorrectParameterException;
  41 +import org.thingsboard.server.dao.model.AssetEntity;
37 42 import org.thingsboard.server.dao.model.RuleMetaDataEntity;
38 43 import org.thingsboard.server.dao.plugin.PluginService;
39 44 import org.thingsboard.server.dao.service.DataValidator;
... ... @@ -53,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
53 58
54 59 @Service
55 60 @Slf4j
56   -public class BaseRuleService implements RuleService {
  61 +public class BaseRuleService extends BaseEntityService implements RuleService {
57 62
58 63 private final TenantId systemTenantId = new TenantId(NULL_UUID);
59 64
... ... @@ -167,6 +172,13 @@ public class BaseRuleService implements RuleService {
167 172 }
168 173
169 174 @Override
  175 + public ListenableFuture<RuleMetaData> findRuleByIdAsync(RuleId ruleId) {
  176 + validateId(ruleId, "Incorrect rule id for search rule request.");
  177 + ListenableFuture<RuleMetaDataEntity> ruleEntity = ruleDao.findByIdAsync(ruleId.getId());
  178 + return Futures.transform(ruleEntity, (com.google.common.base.Function<? super RuleMetaDataEntity, ? extends RuleMetaData>) input -> getData(input));
  179 + }
  180 +
  181 + @Override
170 182 public List<RuleMetaData> findPluginRules(String pluginToken) {
171 183 List<RuleMetaDataEntity> ruleEntities = ruleDao.findRulesByPlugin(pluginToken);
172 184 return convertDataList(ruleEntities);
... ... @@ -235,6 +247,7 @@ public class BaseRuleService implements RuleService {
235 247 @Override
236 248 public void deleteRuleById(RuleId ruleId) {
237 249 validateId(ruleId, "Incorrect rule id for delete rule request.");
  250 + deleteEntityRelations(ruleId);
238 251 ruleDao.deleteById(ruleId);
239 252 }
240 253
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.rule;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.id.RuleId;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -29,6 +30,8 @@ public interface RuleService {
29 30
30 31 RuleMetaData findRuleById(RuleId ruleId);
31 32
  33 + ListenableFuture<RuleMetaData> findRuleByIdAsync(RuleId ruleId);
  34 +
32 35 List<RuleMetaData> findPluginRules(String pluginToken);
33 36
34 37 TextPageData<RuleMetaData> findSystemRules(TextPageLink pageLink);
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.service;
17 17
  18 +import org.thingsboard.server.common.data.id.EntityId;
18 19 import org.thingsboard.server.common.data.id.UUIDBased;
19 20 import org.thingsboard.server.common.data.page.TextPageLink;
20 21 import org.thingsboard.server.dao.exception.IncorrectParameterException;
... ... @@ -25,6 +26,19 @@ import java.util.UUID;
25 26 public class Validator {
26 27
27 28 /**
  29 + * This method validate <code>EntityId</code> entity id. If entity id is invalid than throw
  30 + * <code>IncorrectParameterException</code> exception
  31 + *
  32 + * @param entityId the entityId
  33 + * @param errorMessage the error message for exception
  34 + */
  35 + public static void validateEntityId(EntityId entityId, String errorMessage) {
  36 + if (entityId == null || entityId.getId() == null) {
  37 + throw new IncorrectParameterException(errorMessage);
  38 + }
  39 + }
  40 +
  41 + /**
28 42 * This method validate <code>String</code> string. If string is invalid than throw
29 43 * <code>IncorrectParameterException</code> exception
30 44 *
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.dao.tenant;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.Tenant;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -22,16 +23,16 @@ import org.thingsboard.server.common.data.page.TextPageLink;
22 23
23 24 public interface TenantService {
24 25
25   - public Tenant findTenantById(TenantId tenantId);
26   -
27   - public Tenant saveTenant(Tenant tenant);
  26 + Tenant findTenantById(TenantId tenantId);
  27 +
  28 + ListenableFuture<Tenant> findTenantByIdAsync(TenantId customerId);
28 29
29   - public void deleteTenant(TenantId tenantId);
  30 + Tenant saveTenant(Tenant tenant);
30 31
31   - public TextPageData<Tenant> findTenants(TextPageLink pageLink);
  32 + void deleteTenant(TenantId tenantId);
32 33
33   - //public TextPageData<Tenant> findTenantsByTitle(String title, PageLink pageLink);
  34 + TextPageData<Tenant> findTenants(TextPageLink pageLink);
34 35
35   - public void deleteTenants();
  36 + void deleteTenants();
36 37
37 38 }
... ...
... ... @@ -17,11 +17,16 @@ package org.thingsboard.server.dao.tenant;
17 17
18 18 import static org.thingsboard.server.dao.DaoUtil.convertDataList;
19 19 import static org.thingsboard.server.dao.DaoUtil.getData;
  20 +import static org.thingsboard.server.dao.service.Validator.validateId;
20 21
21 22 import java.util.List;
22 23
  24 +import com.google.common.base.Function;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
23 27 import lombok.extern.slf4j.Slf4j;
24 28 import org.apache.commons.lang3.StringUtils;
  29 +import org.thingsboard.server.common.data.Customer;
25 30 import org.thingsboard.server.common.data.Tenant;
26 31 import org.thingsboard.server.common.data.id.TenantId;
27 32 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -29,15 +34,15 @@ import org.thingsboard.server.common.data.page.TextPageLink;
29 34 import org.thingsboard.server.dao.customer.CustomerService;
30 35 import org.thingsboard.server.dao.dashboard.DashboardService;
31 36 import org.thingsboard.server.dao.device.DeviceService;
  37 +import org.thingsboard.server.dao.entity.BaseEntityService;
32 38 import org.thingsboard.server.dao.exception.DataValidationException;
  39 +import org.thingsboard.server.dao.model.CustomerEntity;
33 40 import org.thingsboard.server.dao.model.TenantEntity;
34 41 import org.thingsboard.server.dao.plugin.PluginService;
35 42 import org.thingsboard.server.dao.rule.RuleService;
36 43 import org.thingsboard.server.dao.service.DataValidator;
37 44 import org.thingsboard.server.dao.service.PaginatedRemover;
38 45 import org.thingsboard.server.dao.user.UserService;
39   -import org.slf4j.Logger;
40   -import org.slf4j.LoggerFactory;
41 46 import org.springframework.beans.factory.annotation.Autowired;
42 47 import org.springframework.stereotype.Service;
43 48 import org.thingsboard.server.dao.service.Validator;
... ... @@ -45,19 +50,19 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
45 50
46 51 @Service
47 52 @Slf4j
48   -public class TenantServiceImpl implements TenantService {
49   -
  53 +public class TenantServiceImpl extends BaseEntityService implements TenantService {
  54 +
50 55 private static final String DEFAULT_TENANT_REGION = "Global";
51 56
52 57 @Autowired
53 58 private TenantDao tenantDao;
54   -
  59 +
55 60 @Autowired
56 61 private UserService userService;
57   -
  62 +
58 63 @Autowired
59 64 private CustomerService customerService;
60   -
  65 +
61 66 @Autowired
62 67 private DeviceService deviceService;
63 68
... ... @@ -72,7 +77,7 @@ public class TenantServiceImpl implements TenantService {
72 77
73 78 @Autowired
74 79 private PluginService pluginService;
75   -
  80 +
76 81 @Override
77 82 public Tenant findTenantById(TenantId tenantId) {
78 83 log.trace("Executing findTenantById [{}]", tenantId);
... ... @@ -82,6 +87,14 @@ public class TenantServiceImpl implements TenantService {
82 87 }
83 88
84 89 @Override
  90 + public ListenableFuture<Tenant> findTenantByIdAsync(TenantId tenantId) {
  91 + log.trace("Executing TenantIdAsync [{}]", tenantId);
  92 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  93 + ListenableFuture<TenantEntity> tenantEntity = tenantDao.findByIdAsync(tenantId.getId());
  94 + return Futures.transform(tenantEntity, (Function<? super TenantEntity, ? extends Tenant>) input -> getData(input));
  95 + }
  96 +
  97 + @Override
85 98 public Tenant saveTenant(Tenant tenant) {
86 99 log.trace("Executing saveTenant [{}]", tenant);
87 100 tenant.setRegion(DEFAULT_TENANT_REGION);
... ... @@ -102,6 +115,7 @@ public class TenantServiceImpl implements TenantService {
102 115 ruleService.deleteRulesByTenantId(tenantId);
103 116 pluginService.deletePluginsByTenantId(tenantId);
104 117 tenantDao.removeById(tenantId.getId());
  118 + deleteEntityRelations(tenantId);
105 119 }
106 120
107 121 @Override
... ... @@ -131,10 +145,10 @@ public class TenantServiceImpl implements TenantService {
131 145 }
132 146 }
133 147 };
134   -
  148 +
135 149 private PaginatedRemover<String, TenantEntity> tenantsRemover =
136 150 new PaginatedRemover<String, TenantEntity>() {
137   -
  151 +
138 152 @Override
139 153 protected List<TenantEntity> findEntities(String region, TextPageLink pageLink) {
140 154 return tenantDao.findTenantsByRegion(region, pageLink);
... ...