From fed0fe8b000a094d37a0a61da770c5485041e32a Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Mon, 19 Feb 2018 19:34:04 +0200 Subject: [PATCH] Redis cache as an option and adding Serializable. --- .../plugin/PluginProcessingContext.java | 4 +- .../server/controller/BaseController.java | 4 + .../controller/EntityRelationController.java | 10 +- .../src/main/resources/thingsboard.yml | 30 ++--- .../server/common/data/ContactBased.java | 2 +- .../server/common/data/Customer.java | 22 +--- .../server/common/data/Device.java | 14 +-- .../server/common/data/HasAdditionalInfo.java | 24 ++++ .../SearchTextBasedWithAdditionalInfo.java | 105 ++++++++++++++++++ .../server/common/data/Tenant.java | 14 +-- .../thingsboard/server/common/data/User.java | 14 +-- .../server/common/data/asset/Asset.java | 16 +-- .../common/data/plugin/PluginMetaData.java | 31 +++--- .../common/data/relation/EntityRelation.java | 22 +++- .../server/common/data/rule/RuleMetaData.java | 49 ++++++-- dao/pom.xml | 8 ++ .../server/dao/alarm/BaseAlarmService.java | 2 +- ...n.java => CaffeineCacheConfiguration.java} | 6 +- .../dao/cache/TBRedisCacheConfiguration.java | 78 +++++++++++++ .../dao/relation/BaseRelationService.java | 103 ++++++++++++----- .../server/dao/relation/RelationService.java | 20 +++- .../dao/service/BaseRelationServiceTest.java | 24 ++-- .../resources/application-test.properties | 25 +++-- docker/docker-compose-tests.yml | 27 +++++ pom.xml | 12 ++ 25 files changed, 486 insertions(+), 180 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/HasAdditionalInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java rename dao/src/main/java/org/thingsboard/server/dao/cache/{ServiceCacheConfiguration.java => CaffeineCacheConfiguration.java} (90%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java create mode 100644 docker/docker-compose-tests.yml diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java index d2d9b7ae10..3f0d39f99e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java @@ -429,12 +429,12 @@ public final class PluginProcessingContext implements PluginContext { @Override public ListenableFuture> findByFromAndType(EntityId from, String relationType) { - return this.pluginCtx.relationService.findByFromAndType(from, relationType, RelationTypeGroup.COMMON); + return this.pluginCtx.relationService.findByFromAndTypeAsync(from, relationType, RelationTypeGroup.COMMON); } @Override public ListenableFuture> findByToAndType(EntityId from, String relationType) { - return this.pluginCtx.relationService.findByToAndType(from, relationType, RelationTypeGroup.COMMON); + return this.pluginCtx.relationService.findByToAndTypeAsync(from, relationType, RelationTypeGroup.COMMON); } @Override diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 16ba93eca8..eb20287514 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -117,6 +117,10 @@ public abstract class BaseController { @Autowired protected RelationService relationService; + @ExceptionHandler(Exception.class) + public void handleException(Exception ex, HttpServletResponse response) { + errorResponseHandler.handle(ex, response); + } @ExceptionHandler(ThingsboardException.class) public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index ec7be1276a..5705ed9773 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -120,7 +120,7 @@ public class EntityRelationController extends BaseController { checkEntityId(fromId); checkEntityId(toId); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); - return checkNotNull(relationService.getRelation(fromId, toId, strRelationType, typeGroup).get()); + return checkNotNull(relationService.getRelation(fromId, toId, strRelationType, typeGroup)); } catch (Exception e) { throw handleException(e); } @@ -138,7 +138,7 @@ public class EntityRelationController extends BaseController { checkEntityId(entityId); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByFrom(entityId, typeGroup).get()); + return checkNotNull(relationService.findByFrom(entityId, typeGroup)); } catch (Exception e) { throw handleException(e); } @@ -176,7 +176,7 @@ public class EntityRelationController extends BaseController { checkEntityId(entityId); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByFromAndType(entityId, strRelationType, typeGroup).get()); + return checkNotNull(relationService.findByFromAndType(entityId, strRelationType, typeGroup)); } catch (Exception e) { throw handleException(e); } @@ -194,7 +194,7 @@ public class EntityRelationController extends BaseController { checkEntityId(entityId); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByTo(entityId, typeGroup).get()); + return checkNotNull(relationService.findByTo(entityId, typeGroup)); } catch (Exception e) { throw handleException(e); } @@ -232,7 +232,7 @@ public class EntityRelationController extends BaseController { checkEntityId(entityId); RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); try { - return checkNotNull(relationService.findByToAndType(entityId, strRelationType, typeGroup).get()); + return checkNotNull(relationService.findByToAndType(entityId, strRelationType, typeGroup)); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 3edea4c367..de1013582c 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -214,26 +214,11 @@ actors: enabled: "${ACTORS_STATISTICS_ENABLED:true}" persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" -# Cache parameters cache: - # Enable/disable cache functionality. - enabled: "${CACHE_ENABLED:false}" - device_credentials: - # Default time to store device credentials in cache, in seconds - time_to_live: "${CACHE_DEVICE_CREDENTIAL_TTL:3600}" - # Maximum size of the map. When maximum size is reached, the map is evicted based on the policy defined. - max_size: - # Max size policy options: - # PER_NODE: Maximum number of map entries in each JVM. - # PER_PARTITION: Maximum number of map entries within each partition. - # USED_HEAP_SIZE: Maximum used heap size in megabytes for each JVM. - # USED_HEAP_PERCENTAGE: Maximum used heap size percentage for each JVM. - # FREE_HEAP_SIZE: Minimum free heap size in megabytes for each JVM. - # FREE_HEAP_PERCENTAGE: Minimum free heap size percentage for each JVM. - policy: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_POLICY:PER_NODE}" - size: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_SIZE:1000000}" + # caffeine or redis + type: "${CACHE_TYPE:caffeine}" -caching: +caffeine: specs: relations: timeToLiveInMinutes: 1440 @@ -245,6 +230,15 @@ caching: timeToLiveInMinutes: 1440 maxSize: 100000 +redis: + # standalone or cluster + connection: + type: standalone + host: "${REDIS_HOST:localhost}" + port: "${REDIS_PORT:6379}" + db: "${REDIS_DB:0}" + password: "${REDIS_PASSWORD:}" + # Check new version updates parameters updates: # Enable/disable updates checking. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java index d5252a0ce1..da0c56b3a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java @@ -19,7 +19,7 @@ import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.UUIDBased; @EqualsAndHashCode(callSuper = true) -public abstract class ContactBased extends SearchTextBased { +public abstract class ContactBased extends SearchTextBasedWithAdditionalInfo { private static final long serialVersionUID = 5047448057830660988L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 7754e88ecd..1ee9cab68e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -29,8 +29,7 @@ public class Customer extends ContactBased implements HasName { private String title; private TenantId tenantId; - private transient JsonNode additionalInfo; - + public Customer() { super(); } @@ -43,7 +42,6 @@ public class Customer extends ContactBased implements HasName { super(customer); this.tenantId = customer.getTenantId(); this.title = customer.getTitle(); - this.additionalInfo = customer.getAdditionalInfo(); } public TenantId getTenantId() { @@ -65,7 +63,7 @@ public class Customer extends ContactBased implements HasName { @JsonIgnore public boolean isPublic() { if (getAdditionalInfo() != null && getAdditionalInfo().has("isPublic")) { - return additionalInfo.get("isPublic").asBoolean(); + return getAdditionalInfo().get("isPublic").asBoolean(); } return false; @@ -77,14 +75,6 @@ public class Customer extends ContactBased implements HasName { return title; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String getSearchText() { return getTitle(); @@ -94,7 +84,6 @@ public class Customer extends ContactBased implements HasName { public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + ((additionalInfo == null) ? 0 : additionalInfo.hashCode()); result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); result = prime * result + ((title == null) ? 0 : title.hashCode()); return result; @@ -109,11 +98,6 @@ public class Customer extends ContactBased implements HasName { if (getClass() != obj.getClass()) return false; Customer other = (Customer) obj; - if (additionalInfo == null) { - if (other.additionalInfo != null) - return false; - } else if (!additionalInfo.equals(other.additionalInfo)) - return false; if (tenantId == null) { if (other.tenantId != null) return false; @@ -135,7 +119,7 @@ public class Customer extends ContactBased implements HasName { builder.append(", tenantId="); builder.append(tenantId); builder.append(", additionalInfo="); - builder.append(additionalInfo); + builder.append(getAdditionalInfo()); builder.append(", country="); builder.append(country); builder.append(", state="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 131a3100c5..9019960127 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; @EqualsAndHashCode(callSuper = true) -public class Device extends SearchTextBased implements HasName { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = 2807343040519543363L; @@ -31,7 +31,6 @@ public class Device extends SearchTextBased implements HasName { private CustomerId customerId; private String name; private String type; - private transient JsonNode additionalInfo; public Device() { super(); @@ -47,7 +46,6 @@ public class Device extends SearchTextBased implements HasName { this.customerId = device.getCustomerId(); this.name = device.getName(); this.type = device.getType(); - this.additionalInfo = device.getAdditionalInfo(); } public TenantId getTenantId() { @@ -83,14 +81,6 @@ public class Device extends SearchTextBased implements HasName { this.type = type; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String getSearchText() { return getName(); @@ -108,7 +98,7 @@ public class Device extends SearchTextBased implements HasName { builder.append(", type="); builder.append(type); builder.append(", additionalInfo="); - builder.append(additionalInfo); + builder.append(getAdditionalInfo()); builder.append(", createdTime="); builder.append(createdTime); builder.append(", id="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasAdditionalInfo.java new file mode 100644 index 0000000000..3239df01b6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasAdditionalInfo.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.databind.JsonNode; + +public interface HasAdditionalInfo { + + JsonNode getAdditionalInfo(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java new file mode 100644 index 0000000000..9dc077a07a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.UUIDBased; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.function.Consumer; + +/** + * Created by ashvayka on 19.02.18. + */ +@Slf4j +public abstract class SearchTextBasedWithAdditionalInfo extends SearchTextBased implements HasAdditionalInfo { + + private transient JsonNode additionalInfo; + @JsonIgnore + private byte[] additionalInfoBytes; + + public SearchTextBasedWithAdditionalInfo() { + super(); + } + + public SearchTextBasedWithAdditionalInfo(I id) { + super(id); + } + + public SearchTextBasedWithAdditionalInfo(SearchTextBasedWithAdditionalInfo searchTextBased) { + super(searchTextBased); + setAdditionalInfo(searchTextBased.getAdditionalInfo()); + } + + @Override + public JsonNode getAdditionalInfo() { + return getJson(() -> additionalInfo, () -> additionalInfoBytes); + } + + public void setAdditionalInfo(JsonNode addInfo) { + setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + SearchTextBasedWithAdditionalInfo that = (SearchTextBasedWithAdditionalInfo) o; + return Arrays.equals(additionalInfoBytes, that.additionalInfoBytes); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), additionalInfoBytes); + } + + public static JsonNode getJson(Supplier jsonData, Supplier binaryData) { + JsonNode json = jsonData.get(); + if (json != null) { + return json; + } else { + byte[] data = binaryData.get(); + if (data != null) { + try { + return new ObjectMapper().readTree(new ByteArrayInputStream(data)); + } catch (IOException e) { + log.warn("Can't deserialize json data: ", e); + return null; + } + } else { + return null; + } + } + } + + public static void setJson(JsonNode json, Consumer jsonConsumer, Consumer bytesConsumer) { + jsonConsumer.accept(json); + try { + bytesConsumer.accept(new ObjectMapper().writeValueAsBytes(json)); + } catch (JsonProcessingException e) { + log.warn("Can't serialize json data: ", e); + } + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index bb209f1011..334ec4d038 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -28,8 +28,7 @@ public class Tenant extends ContactBased implements HasName { private String title; private String region; - private transient JsonNode additionalInfo; - + public Tenant() { super(); } @@ -42,7 +41,6 @@ public class Tenant extends ContactBased implements HasName { super(tenant); this.title = tenant.getTitle(); this.region = tenant.getRegion(); - this.additionalInfo = tenant.getAdditionalInfo(); } public String getTitle() { @@ -67,14 +65,6 @@ public class Tenant extends ContactBased implements HasName { this.region = region; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String getSearchText() { return getTitle(); @@ -88,7 +78,7 @@ public class Tenant extends ContactBased implements HasName { builder.append(", region="); builder.append(region); builder.append(", additionalInfo="); - builder.append(additionalInfo); + builder.append(getAdditionalInfo()); builder.append(", country="); builder.append(country); builder.append(", state="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index a6488995a9..a6f13d0803 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.security.Authority; import com.fasterxml.jackson.databind.JsonNode; @EqualsAndHashCode(callSuper = true) -public class User extends SearchTextBased implements HasName { +public class User extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = 8250339805336035966L; @@ -35,7 +35,6 @@ public class User extends SearchTextBased implements HasName { private Authority authority; private String firstName; private String lastName; - private transient JsonNode additionalInfo; public User() { super(); @@ -53,7 +52,6 @@ public class User extends SearchTextBased implements HasName { this.authority = user.getAuthority(); this.firstName = user.getFirstName(); this.lastName = user.getLastName(); - this.additionalInfo = user.getAdditionalInfo(); } public TenantId getTenantId() { @@ -110,14 +108,6 @@ public class User extends SearchTextBased implements HasName { this.lastName = lastName; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String getSearchText() { return getEmail(); @@ -139,7 +129,7 @@ public class User extends SearchTextBased implements HasName { builder.append(", lastName="); builder.append(lastName); builder.append(", additionalInfo="); - builder.append(additionalInfo); + builder.append(getAdditionalInfo()); builder.append(", createdTime="); builder.append(createdTime); builder.append(", id="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 382a2d72e2..2ed7f5d9fe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -17,14 +17,16 @@ package org.thingsboard.server.common.data.asset; import com.fasterxml.jackson.databind.JsonNode; import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.HasAdditionalInfo; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) -public class Asset extends SearchTextBased implements HasName { +public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = 2807343040519543363L; @@ -32,7 +34,6 @@ public class Asset extends SearchTextBased implements HasName { private CustomerId customerId; private String name; private String type; - private transient JsonNode additionalInfo; public Asset() { super(); @@ -48,7 +49,6 @@ public class Asset extends SearchTextBased implements HasName { this.customerId = asset.getCustomerId(); this.name = asset.getName(); this.type = asset.getType(); - this.additionalInfo = asset.getAdditionalInfo(); } public TenantId getTenantId() { @@ -84,14 +84,6 @@ public class Asset extends SearchTextBased implements HasName { this.type = type; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String getSearchText() { return getName(); @@ -109,7 +101,7 @@ public class Asset extends SearchTextBased implements HasName { builder.append(", type="); builder.append(type); builder.append(", additionalInfo="); - builder.append(additionalInfo); + builder.append(getAdditionalInfo()); builder.append(", createdTime="); builder.append(createdTime); builder.append(", id="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java index 755cf71bab..4b91752b4b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java @@ -15,16 +15,24 @@ */ package org.thingsboard.server.common.data.plugin; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.PluginId; import org.thingsboard.server.common.data.id.TenantId; import com.fasterxml.jackson.databind.JsonNode; +import java.io.ByteArrayInputStream; +import java.io.IOException; + @EqualsAndHashCode(callSuper = true) -public class PluginMetaData extends SearchTextBased implements HasName { +@Slf4j +public class PluginMetaData extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = 1L; @@ -35,7 +43,9 @@ public class PluginMetaData extends SearchTextBased implements HasName private boolean publicAccess; private ComponentLifecycleState state; private transient JsonNode configuration; - private transient JsonNode additionalInfo; + @JsonIgnore + private byte[] configurationBytes; + public PluginMetaData() { super(); @@ -54,7 +64,6 @@ public class PluginMetaData extends SearchTextBased implements HasName this.publicAccess = plugin.isPublicAccess(); this.state = plugin.getState(); this.configuration = plugin.getConfiguration(); - this.additionalInfo = plugin.getAdditionalInfo(); } @Override @@ -96,11 +105,11 @@ public class PluginMetaData extends SearchTextBased implements HasName } public JsonNode getConfiguration() { - return configuration; + return getJson(() -> configuration, () -> configurationBytes); } - public void setConfiguration(JsonNode configuration) { - this.configuration = configuration; + public void setConfiguration(JsonNode data) { + setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes); } public boolean isPublicAccess() { @@ -119,14 +128,6 @@ public class PluginMetaData extends SearchTextBased implements HasName return state; } - public JsonNode getAdditionalInfo() { - return additionalInfo; - } - - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; - } - @Override public String toString() { return "PluginMetaData [apiToken=" + apiToken + ", tenantId=" + tenantId + ", name=" + name + ", clazz=" + clazz + ", publicAccess=" + publicAccess diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java index f9d70fa874..dcb7f2fd71 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java @@ -15,10 +15,20 @@ */ package org.thingsboard.server.common.data.relation; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.EntityId; -public class EntityRelation { +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Serializable; + +@Slf4j +public class EntityRelation implements Serializable { private static final long serialVersionUID = 2807343040519543363L; @@ -29,7 +39,9 @@ public class EntityRelation { private EntityId to; private String type; private RelationTypeGroup typeGroup; - private JsonNode additionalInfo; + private transient JsonNode additionalInfo; + @JsonIgnore + private byte[] additionalInfoBytes; public EntityRelation() { super(); @@ -92,11 +104,11 @@ public class EntityRelation { } public JsonNode getAdditionalInfo() { - return additionalInfo; + return SearchTextBasedWithAdditionalInfo.getJson(() -> additionalInfo, () -> additionalInfoBytes); } - public void setAdditionalInfo(JsonNode additionalInfo) { - this.additionalInfo = additionalInfo; + public void setAdditionalInfo(JsonNode addInfo) { + SearchTextBasedWithAdditionalInfo.setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java index 1fd72c2246..d2d6419138 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java @@ -15,18 +15,23 @@ */ package org.thingsboard.server.common.data.rule; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.SearchTextBased; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; @Data @EqualsAndHashCode(callSuper = true) -public class RuleMetaData extends SearchTextBased implements HasName { +@Slf4j +public class RuleMetaData extends SearchTextBasedWithAdditionalInfo implements HasName { private static final long serialVersionUID = -5656679015122935465L; @@ -38,7 +43,13 @@ public class RuleMetaData extends SearchTextBased implements HasName { private transient JsonNode filters; private transient JsonNode processor; private transient JsonNode action; - private transient JsonNode additionalInfo; + @JsonIgnore + private byte[] filtersBytes; + @JsonIgnore + private byte[] processorBytes; + @JsonIgnore + private byte[] actionBytes; + public RuleMetaData() { super(); @@ -55,10 +66,9 @@ public class RuleMetaData extends SearchTextBased implements HasName { this.state = rule.getState(); this.weight = rule.getWeight(); this.pluginToken = rule.getPluginToken(); - this.filters = rule.getFilters(); - this.processor = rule.getProcessor(); - this.action = rule.getAction(); - this.additionalInfo = rule.getAdditionalInfo(); + this.setFilters(rule.getFilters()); + this.setProcessor(rule.getProcessor()); + this.setAction(rule.getAction()); } @Override @@ -71,4 +81,29 @@ public class RuleMetaData extends SearchTextBased implements HasName { return name; } + public JsonNode getFilters() { + return SearchTextBasedWithAdditionalInfo.getJson(() -> filters, () -> filtersBytes); + } + + public JsonNode getProcessor() { + return SearchTextBasedWithAdditionalInfo.getJson(() -> processor, () -> processorBytes); + } + + public JsonNode getAction() { + return SearchTextBasedWithAdditionalInfo.getJson(() -> action, () -> actionBytes); + } + + public void setFilters(JsonNode data) { + setJson(data, json -> this.filters = json, bytes -> this.filtersBytes = bytes); + } + + public void setProcessor(JsonNode data) { + setJson(data, json -> this.processor = json, bytes -> this.processorBytes = bytes); + } + + public void setAction(JsonNode data) { + setJson(data, json -> this.action = json, bytes -> this.actionBytes = bytes); + } + + } diff --git a/dao/pom.xml b/dao/pom.xml index 75d69343e5..0e72a6c7d6 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -182,6 +182,14 @@ org.springframework spring-context-support + + org.springframework.data + spring-data-redis + + + redis.clients + jedis + diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index be7e44f008..02bf108cff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -337,7 +337,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) { try { - List relations = relationService.findByTo(alarm.getId(), RelationTypeGroup.ALARM).get(); + List relations = relationService.findByToAsync(alarm.getId(), RelationTypeGroup.ALARM).get(); Set parents = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet()); for (EntityId parentId : parents) { updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus); diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/ServiceCacheConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java similarity index 90% rename from dao/src/main/java/org/thingsboard/server/dao/cache/ServiceCacheConfiguration.java rename to dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java index ba21cb204c..b65a54fa2f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cache/ServiceCacheConfiguration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/CaffeineCacheConfiguration.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Ticker; import lombok.Data; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; @@ -33,10 +34,11 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Configuration -@ConfigurationProperties(prefix = "caching") +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@ConfigurationProperties(prefix = "caffeine") @EnableCaching @Data -public class ServiceCacheConfiguration { +public class CaffeineCacheConfiguration { private Map specs; diff --git a/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java new file mode 100644 index 0000000000..340e25f7a9 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java @@ -0,0 +1,78 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.cache; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; + +@Configuration +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis", matchIfMissing = false) +@EnableCaching +@Data +public class TBRedisCacheConfiguration { + + @Value("${redis.connection.host}") + private String host; + + @Value("${redis.connection.port}") + private Integer port; + + @Value("${redis.connection.db}") + private Integer db; + + @Value("${redis.connection.password}") + private String password; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + JedisConnectionFactory factory = new JedisConnectionFactory(); + factory.setHostName(host); + factory.setPort(port); + factory.setDatabase(db); + factory.setPassword(password); + return factory; + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory cf) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(cf); + return redisTemplate; + } + + @Bean + public CacheManager cacheManager(RedisTemplate redisTemplate) { + return new RedisCacheManager(redisTemplate); + } + + @Bean + public KeyGenerator previousDeviceCredentialsId() { + return new PreviousDeviceCredentialsIdKeyGenerator(); + } + + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 6e3e75b7e9..8e3252d5ac 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -64,19 +64,28 @@ public class BaseRelationService implements RelationService { return relationDao.checkRelation(from, to, relationType, typeGroup); } - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType}") + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}") @Override - public ListenableFuture getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + public EntityRelation getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { + try { + return getRelationAsync(from, to, relationType, typeGroup).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public ListenableFuture getRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { log.trace("Executing EntityRelation [{}][{}][{}][{}]", from, to, relationType, typeGroup); validate(from, to, relationType, typeGroup); return relationDao.getRelation(from, to, relationType, typeGroup); } @Caching(evict = { - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") }) @Override public boolean saveRelation(EntityRelation relation) { @@ -86,10 +95,10 @@ public class BaseRelationService implements RelationService { } @Caching(evict = { - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}") + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") }) @Override public ListenableFuture saveRelationAsync(EntityRelation relation) { @@ -99,11 +108,11 @@ public class BaseRelationService implements RelationService { } @Caching(evict = { - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.from"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}"), + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}") }) @Override public boolean deleteRelation(EntityRelation relation) { @@ -117,7 +126,7 @@ public class BaseRelationService implements RelationService { @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type}"), @CacheEvict(cacheNames = RELATIONS_CACHE, key = "#relation.to"), @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type}"), - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type}") + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}") }) @Override public ListenableFuture deleteRelationAsync(EntityRelation relation) { @@ -218,9 +227,9 @@ public class BaseRelationService implements RelationService { ListenableFuture>> inboundRelationsTo = Futures.allAsList(inboundRelationsListTo); ListenableFuture> inboundDeletions = Futures.transform(inboundRelationsTo, (AsyncFunction>, List>) relations -> { - List> results = getListenableFutures(relations, cache, true); - return Futures.allAsList(results); - }); + List> results = getListenableFutures(relations, cache, true); + return Futures.allAsList(results); + }); ListenableFuture inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); @@ -272,9 +281,18 @@ public class BaseRelationService implements RelationService { cache.evict(fromToAndType); } - @Cacheable(cacheNames = RELATIONS_CACHE, key = "#from") + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}") + @Override + public List findByFrom(EntityId from, RelationTypeGroup typeGroup) { + try { + return findByFromAsync(from, typeGroup).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + @Override - public ListenableFuture> findByFrom(EntityId from, RelationTypeGroup typeGroup) { + public ListenableFuture> findByFromAsync(EntityId from, RelationTypeGroup typeGroup) { log.trace("Executing findByFrom [{}][{}]", from, typeGroup); validate(from); validateTypeGroup(typeGroup); @@ -300,9 +318,18 @@ public class BaseRelationService implements RelationService { return relationsInfo; } - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType}") + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}") + @Override + public List findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { + try { + return findByFromAndTypeAsync(from, relationType, typeGroup).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + @Override - public ListenableFuture> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { + public ListenableFuture> findByFromAndTypeAsync(EntityId from, String relationType, RelationTypeGroup typeGroup) { log.trace("Executing findByFromAndType [{}][{}][{}]", from, relationType, typeGroup); validate(from); validateType(relationType); @@ -310,9 +337,18 @@ public class BaseRelationService implements RelationService { return relationDao.findAllByFromAndType(from, relationType, typeGroup); } - @Cacheable(cacheNames = RELATIONS_CACHE, key = "#to") + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}") + @Override + public List findByTo(EntityId to, RelationTypeGroup typeGroup) { + try { + return findByToAsync(to, typeGroup).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + @Override - public ListenableFuture> findByTo(EntityId to, RelationTypeGroup typeGroup) { + public ListenableFuture> findByToAsync(EntityId to, RelationTypeGroup typeGroup) { log.trace("Executing findByTo [{}][{}]", to, typeGroup); validate(to); validateTypeGroup(typeGroup); @@ -351,9 +387,18 @@ public class BaseRelationService implements RelationService { return entityRelationInfo; } - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType}") + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") + @Override + public List findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { + try { + return findByToAndTypeAsync(to, relationType, typeGroup).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + @Override - public ListenableFuture> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { + public ListenableFuture> findByToAndTypeAsync(EntityId to, String relationType, RelationTypeGroup typeGroup) { log.trace("Executing findByToAndType [{}][{}][{}]", to, relationType, typeGroup); validate(to); validateType(relationType); @@ -527,9 +572,9 @@ public class BaseRelationService implements RelationService { private ListenableFuture> findRelations(final EntityId rootId, final EntitySearchDirection direction) { ListenableFuture> relations; if (direction == EntitySearchDirection.FROM) { - relations = findByFrom(rootId, RelationTypeGroup.COMMON); + relations = findByFromAsync(rootId, RelationTypeGroup.COMMON); } else { - relations = findByTo(rootId, RelationTypeGroup.COMMON); + relations = findByToAsync(rootId, RelationTypeGroup.COMMON); } return relations; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index cef93935e6..17a459a851 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -31,7 +31,9 @@ public interface RelationService { ListenableFuture checkRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); - ListenableFuture getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); + EntityRelation getRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); + + ListenableFuture getRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); boolean saveRelation(EntityRelation relation); @@ -49,17 +51,25 @@ public interface RelationService { ListenableFuture deleteEntityRelationsAsync(EntityId entity); - ListenableFuture> findByFrom(EntityId from, RelationTypeGroup typeGroup); + List findByFrom(EntityId from, RelationTypeGroup typeGroup); + + ListenableFuture> findByFromAsync(EntityId from, RelationTypeGroup typeGroup); ListenableFuture> findInfoByFrom(EntityId from, RelationTypeGroup typeGroup); - ListenableFuture> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup); + List findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup); + + ListenableFuture> findByFromAndTypeAsync(EntityId from, String relationType, RelationTypeGroup typeGroup); - ListenableFuture> findByTo(EntityId to, RelationTypeGroup typeGroup); + List findByTo(EntityId to, RelationTypeGroup typeGroup); + + ListenableFuture> findByToAsync(EntityId to, RelationTypeGroup typeGroup); ListenableFuture> findInfoByTo(EntityId to, RelationTypeGroup typeGroup); - ListenableFuture> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); + List findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup); + + ListenableFuture> findByToAndTypeAsync(EntityId to, String relationType, RelationTypeGroup typeGroup); ListenableFuture> findByQuery(EntityRelationsQuery query); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java index 6b58af8e19..d1c2ae2af1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRelationServiceTest.java @@ -122,7 +122,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { saveRelation(relationB1); saveRelation(relationB2); - List relations = relationService.findByFrom(parentA, RelationTypeGroup.COMMON).get(); + List relations = relationService.findByFrom(parentA, RelationTypeGroup.COMMON); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.CONTAINS_TYPE, relation.getType()); @@ -130,13 +130,13 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(2, relations.size()); - relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByFromAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFrom(parentB, RelationTypeGroup.COMMON).get(); + relations = relationService.findByFrom(parentB, RelationTypeGroup.COMMON); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(EntityRelation.MANAGES_TYPE, relation.getType()); @@ -144,10 +144,10 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { Assert.assertTrue(childA.equals(relation.getTo()) || childB.equals(relation.getTo())); } - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(0, relations.size()); - relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByFromAndType(parentB, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(0, relations.size()); } @@ -177,26 +177,26 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { // Data propagation to views is async Thread.sleep(3000); - List relations = relationService.findByTo(childA, RelationTypeGroup.COMMON).get(); + List relations = relationService.findByTo(childA, RelationTypeGroup.COMMON); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childA, relation.getTo()); Assert.assertTrue(parentA.equals(relation.getFrom()) || parentB.equals(relation.getFrom())); } - relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByToAndType(childA, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByToAndType(childB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(1, relations.size()); - relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByToAndType(parentA, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(0, relations.size()); - relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON).get(); + relations = relationService.findByToAndType(parentB, EntityRelation.MANAGES_TYPE, RelationTypeGroup.COMMON); Assert.assertEquals(0, relations.size()); - relations = relationService.findByTo(childB, RelationTypeGroup.COMMON).get(); + relations = relationService.findByTo(childB, RelationTypeGroup.COMMON); Assert.assertEquals(2, relations.size()); for (EntityRelation relation : relations) { Assert.assertEquals(childB, relation.getTo()); diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index c615df58d2..3466d6675f 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -1,19 +1,22 @@ -cache.enabled=false -cache.device_credentials.time_to_live=3600 -cache.device_credentials.max_size.size=1000000 -cache.device_credentials.max_size.policy=PER_NODE - zk.enabled=false zk.url=localhost:2181 zk.zk_dir=/thingsboard updates.enabled=false -caching.specs.relations.timeToLiveInMinutes=1440 -caching.specs.relations.maxSize=100000 +cache.type=caffeine +#cache.type=redis + +caffeine.specs.relations.timeToLiveInMinutes=1440 +caffeine.specs.relations.maxSize=100000 + +caffeine.specs.deviceCredentials.timeToLiveInMinutes=1440 +caffeine.specs.deviceCredentials.maxSize=100000 -caching.specs.deviceCredentials.timeToLiveInMinutes=1440 -caching.specs.deviceCredentials.maxSize=100000 +caffeine.specs.devices.timeToLiveInMinutes=1440 +caffeine.specs.devices.maxSize=100000 -caching.specs.devices.timeToLiveInMinutes=1440 -caching.specs.devices.maxSize=100000 \ No newline at end of file +redis.connection.host=localhost +redis.connection.port=6379 +redis.connection.db=0 +redis.connection.password= \ No newline at end of file diff --git a/docker/docker-compose-tests.yml b/docker/docker-compose-tests.yml new file mode 100644 index 0000000000..2ef4631a8b --- /dev/null +++ b/docker/docker-compose-tests.yml @@ -0,0 +1,27 @@ +# +# Copyright © 2016-2017 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: '3.3' +services: + redis: + image: redis:4.0 + networks: + - core + ports: + - "6379:6379" + +networks: + core: diff --git a/pom.xml b/pom.xml index dd3cad9a6c..80e846d56f 100755 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,8 @@ 1.4.3.RELEASE 4.3.4.RELEASE 4.2.0.RELEASE + 1.8.10.RELEASE + 2.9.0 0.7.0 2.2.0 4.12 @@ -783,6 +785,16 @@ 2.2 test + + org.springframework.data + spring-data-redis + ${spring-data-redis.version} + + + redis.clients + jedis + ${jedis.version} + com.sun.winsw winsw