56 changed files with 1705 additions and 252 deletions
@ -0,0 +1,89 @@ |
|||
-- |
|||
-- 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. |
|||
-- |
|||
|
|||
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id ( |
|||
tenant_id timeuuid, |
|||
id timeuuid, |
|||
customer_id timeuuid, |
|||
entity_id timeuuid, |
|||
entity_type text, |
|||
entity_name text, |
|||
user_id timeuuid, |
|||
user_name text, |
|||
action_type text, |
|||
action_data text, |
|||
action_status text, |
|||
action_failure_details text, |
|||
PRIMARY KEY ((tenant_id, entity_id, entity_type), id) |
|||
); |
|||
|
|||
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_customer_id ( |
|||
tenant_id timeuuid, |
|||
id timeuuid, |
|||
customer_id timeuuid, |
|||
entity_id timeuuid, |
|||
entity_type text, |
|||
entity_name text, |
|||
user_id timeuuid, |
|||
user_name text, |
|||
action_type text, |
|||
action_data text, |
|||
action_status text, |
|||
action_failure_details text, |
|||
PRIMARY KEY ((tenant_id, customer_id), id) |
|||
); |
|||
|
|||
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id ( |
|||
tenant_id timeuuid, |
|||
id timeuuid, |
|||
customer_id timeuuid, |
|||
entity_id timeuuid, |
|||
entity_type text, |
|||
entity_name text, |
|||
user_id timeuuid, |
|||
user_name text, |
|||
action_type text, |
|||
action_data text, |
|||
action_status text, |
|||
action_failure_details text, |
|||
PRIMARY KEY ((tenant_id, user_id), id) |
|||
); |
|||
|
|||
|
|||
|
|||
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id ( |
|||
tenant_id timeuuid, |
|||
id timeuuid, |
|||
partition bigint, |
|||
customer_id timeuuid, |
|||
entity_id timeuuid, |
|||
entity_type text, |
|||
entity_name text, |
|||
user_id timeuuid, |
|||
user_name text, |
|||
action_type text, |
|||
action_data text, |
|||
action_status text, |
|||
action_failure_details text, |
|||
PRIMARY KEY ((tenant_id, partition), id) |
|||
); |
|||
|
|||
CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( |
|||
tenant_id timeuuid, |
|||
partition bigint, |
|||
PRIMARY KEY (( tenant_id ), partition) |
|||
) WITH CLUSTERING ORDER BY ( partition ASC ) |
|||
AND compaction = { 'class' : 'LeveledCompactionStrategy' }; |
|||
@ -0,0 +1,31 @@ |
|||
-- |
|||
-- 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. |
|||
-- |
|||
|
|||
CREATE TABLE IF NOT EXISTS audit_log ( |
|||
id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY, |
|||
tenant_id varchar(31), |
|||
customer_id varchar(31), |
|||
entity_id varchar(31), |
|||
entity_type varchar(255), |
|||
entity_name varchar(255), |
|||
user_id varchar(31), |
|||
user_name varchar(255), |
|||
action_type varchar(255), |
|||
action_data varchar(255), |
|||
action_status varchar(255), |
|||
action_failure_details varchar |
|||
); |
|||
|
|||
@ -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(); |
|||
|
|||
} |
|||
@ -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<I extends UUIDBased> extends SearchTextBased<I> implements HasAdditionalInfo { |
|||
|
|||
private transient JsonNode additionalInfo; |
|||
@JsonIgnore |
|||
private byte[] additionalInfoBytes; |
|||
|
|||
public SearchTextBasedWithAdditionalInfo() { |
|||
super(); |
|||
} |
|||
|
|||
public SearchTextBasedWithAdditionalInfo(I id) { |
|||
super(id); |
|||
} |
|||
|
|||
public SearchTextBasedWithAdditionalInfo(SearchTextBasedWithAdditionalInfo<I> 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<JsonNode> jsonData, Supplier<byte[]> 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<JsonNode> jsonConsumer, Consumer<byte[]> bytesConsumer) { |
|||
jsonConsumer.accept(json); |
|||
try { |
|||
bytesConsumer.accept(new ObjectMapper().writeValueAsBytes(json)); |
|||
} catch (JsonProcessingException e) { |
|||
log.warn("Can't serialize json data: ", e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public final class Clock { |
|||
|
|||
private static long time = 0L; |
|||
|
|||
private Clock() { |
|||
} |
|||
|
|||
|
|||
public static long millis() { |
|||
return time == 0 ? System.currentTimeMillis() : time; |
|||
} |
|||
|
|||
public static void setMillis(long millis) { |
|||
time = millis; |
|||
} |
|||
|
|||
public static void shift(long delta) { |
|||
time += delta; |
|||
} |
|||
|
|||
public static void reset() { |
|||
time = 0; |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
public class HostRequestLimitPolicy { |
|||
|
|||
private final long limit; |
|||
|
|||
public HostRequestLimitPolicy(@Value("${quota.host.limit}") long limit) { |
|||
this.limit = limit; |
|||
} |
|||
|
|||
public boolean isValid(long currentValue) { |
|||
return currentValue <= limit; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Service |
|||
@Slf4j |
|||
public class HostRequestsQuotaService implements QuotaService { |
|||
|
|||
private final HostRequestIntervalRegistry requestRegistry; |
|||
private final HostRequestLimitPolicy requestsPolicy; |
|||
private final IntervalRegistryCleaner registryCleaner; |
|||
private final IntervalRegistryLogger registryLogger; |
|||
private final boolean enabled; |
|||
|
|||
public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, |
|||
IntervalRegistryCleaner registryCleaner, IntervalRegistryLogger registryLogger, |
|||
@Value("${quota.host.enabled}") boolean enabled) { |
|||
this.requestRegistry = requestRegistry; |
|||
this.requestsPolicy = requestsPolicy; |
|||
this.registryCleaner = registryCleaner; |
|||
this.registryLogger = registryLogger; |
|||
this.enabled = enabled; |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (enabled) { |
|||
registryCleaner.schedule(); |
|||
registryLogger.schedule(); |
|||
} |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void close() { |
|||
if (enabled) { |
|||
registryCleaner.stop(); |
|||
registryLogger.stop(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isQuotaExceeded(String key) { |
|||
if (enabled) { |
|||
long count = requestRegistry.tick(key); |
|||
return !requestsPolicy.isValid(count); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public interface QuotaService { |
|||
|
|||
boolean isQuotaExceeded(String key); |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.Sets; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
@Slf4j |
|||
public class HostRequestIntervalRegistry { |
|||
|
|||
private final Map<String, IntervalCount> hostCounts = new ConcurrentHashMap<>(); |
|||
private final long intervalDurationMs; |
|||
private final long ttlMs; |
|||
private final Set<String> whiteList; |
|||
private final Set<String> blackList; |
|||
|
|||
public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, |
|||
@Value("${quota.host.ttlMs}") long ttlMs, |
|||
@Value("${quota.host.whitelist}") String whiteList, |
|||
@Value("${quota.host.blacklist}") String blackList) { |
|||
this.intervalDurationMs = intervalDurationMs; |
|||
this.ttlMs = ttlMs; |
|||
this.whiteList = Sets.newHashSet(StringUtils.split(whiteList, ',')); |
|||
this.blackList = Sets.newHashSet(StringUtils.split(blackList, ',')); |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (ttlMs < intervalDurationMs) { |
|||
log.warn("TTL for IntervalRegistry [{}] smaller than interval duration [{}]", ttlMs, intervalDurationMs); |
|||
} |
|||
log.info("Start Host Quota Service with whitelist {}", whiteList); |
|||
log.info("Start Host Quota Service with blacklist {}", blackList); |
|||
} |
|||
|
|||
public long tick(String clientHostId) { |
|||
if (whiteList.contains(clientHostId)) { |
|||
return 0; |
|||
} else if (blackList.contains(clientHostId)) { |
|||
return Long.MAX_VALUE; |
|||
} |
|||
IntervalCount intervalCount = hostCounts.computeIfAbsent(clientHostId, s -> new IntervalCount(intervalDurationMs)); |
|||
return intervalCount.resetIfExpiredAndTick(); |
|||
} |
|||
|
|||
public void clean() { |
|||
hostCounts.entrySet().removeIf(entry -> entry.getValue().silenceDuration() > ttlMs); |
|||
} |
|||
|
|||
public Map<String, Long> getContent() { |
|||
return hostCounts.entrySet().stream() |
|||
.collect(Collectors.toMap( |
|||
Map.Entry::getKey, |
|||
interval -> interval.getValue().getCount())); |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
|
|||
import org.thingsboard.server.common.transport.quota.Clock; |
|||
|
|||
import java.util.concurrent.atomic.LongAdder; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalCount { |
|||
|
|||
private final LongAdder adder = new LongAdder(); |
|||
private final long intervalDurationMs; |
|||
private volatile long startTime; |
|||
private volatile long lastTickTime; |
|||
|
|||
public IntervalCount(long intervalDurationMs) { |
|||
this.intervalDurationMs = intervalDurationMs; |
|||
startTime = Clock.millis(); |
|||
} |
|||
|
|||
public long resetIfExpiredAndTick() { |
|||
if (isExpired()) { |
|||
reset(); |
|||
} |
|||
tick(); |
|||
return adder.sum(); |
|||
} |
|||
|
|||
public long silenceDuration() { |
|||
return Clock.millis() - lastTickTime; |
|||
} |
|||
|
|||
public long getCount() { |
|||
return adder.sum(); |
|||
} |
|||
|
|||
private void tick() { |
|||
adder.add(1); |
|||
lastTickTime = Clock.millis(); |
|||
} |
|||
|
|||
private void reset() { |
|||
adder.reset(); |
|||
startTime = Clock.millis(); |
|||
} |
|||
|
|||
private boolean isExpired() { |
|||
return (Clock.millis() - startTime) > intervalDurationMs; |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PreDestroy; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
@Slf4j |
|||
public class IntervalRegistryCleaner { |
|||
|
|||
private final HostRequestIntervalRegistry intervalRegistry; |
|||
private final long cleanPeriodMs; |
|||
private ScheduledExecutorService executor; |
|||
|
|||
public IntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, @Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { |
|||
this.intervalRegistry = intervalRegistry; |
|||
this.cleanPeriodMs = cleanPeriodMs; |
|||
} |
|||
|
|||
public void schedule() { |
|||
if (executor != null) { |
|||
throw new IllegalStateException("Registry Cleaner already scheduled"); |
|||
} |
|||
executor = Executors.newSingleThreadScheduledExecutor(); |
|||
executor.scheduleAtFixedRate(this::clean, cleanPeriodMs, cleanPeriodMs, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
public void stop() { |
|||
if (executor != null) { |
|||
executor.shutdown(); |
|||
} |
|||
} |
|||
|
|||
public void clean() { |
|||
try { |
|||
intervalRegistry.clean(); |
|||
} catch (RuntimeException ex) { |
|||
log.error("Could not clear Interval Registry", ex); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.MinMaxPriorityQueue; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.Comparator; |
|||
import java.util.Map; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
@Slf4j |
|||
public class IntervalRegistryLogger { |
|||
|
|||
private final int topSize; |
|||
private final HostRequestIntervalRegistry intervalRegistry; |
|||
private final long logIntervalMin; |
|||
private ScheduledExecutorService executor; |
|||
|
|||
public IntervalRegistryLogger(@Value("${quota.log.topSize}") int topSize, @Value("${quota.log.intervalMin}") long logIntervalMin, |
|||
HostRequestIntervalRegistry intervalRegistry) { |
|||
this.topSize = topSize; |
|||
this.logIntervalMin = logIntervalMin; |
|||
this.intervalRegistry = intervalRegistry; |
|||
} |
|||
|
|||
public void schedule() { |
|||
if (executor != null) { |
|||
throw new IllegalStateException("Registry Cleaner already scheduled"); |
|||
} |
|||
executor = Executors.newSingleThreadScheduledExecutor(); |
|||
executor.scheduleAtFixedRate(this::logStatistic, logIntervalMin, logIntervalMin, TimeUnit.MINUTES); |
|||
} |
|||
|
|||
public void stop() { |
|||
if (executor != null) { |
|||
executor.shutdown(); |
|||
} |
|||
} |
|||
|
|||
public void logStatistic() { |
|||
Map<String, Long> registryContent = intervalRegistry.getContent(); |
|||
int uniqHosts = registryContent.size(); |
|||
long requestsCount = registryContent.values().stream().mapToLong(i -> i).sum(); |
|||
Map<String, Long> top = getTopElements(registryContent); |
|||
log(top, uniqHosts, requestsCount); |
|||
} |
|||
|
|||
protected Map<String, Long> getTopElements(Map<String, Long> countMap) { |
|||
MinMaxPriorityQueue<Map.Entry<String, Long>> topQueue = MinMaxPriorityQueue |
|||
.orderedBy(Comparator.comparing((Function<Map.Entry<String, Long>, Long>) Map.Entry::getValue).reversed()) |
|||
.maximumSize(topSize) |
|||
.create(countMap.entrySet()); |
|||
|
|||
return topQueue.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|||
} |
|||
|
|||
private void log(Map<String, Long> top, int uniqHosts, long requestsCount) { |
|||
long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); |
|||
StringBuilder builder = new StringBuilder("Quota Statistic : "); |
|||
builder.append("uniqHosts : ").append(uniqHosts).append("; "); |
|||
builder.append("requestsCount : ").append(requestsCount).append("; "); |
|||
builder.append("RPS : ").append(rps).append(" "); |
|||
builder.append("top -> "); |
|||
for (Map.Entry<String, Long> host : top.entrySet()) { |
|||
builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); |
|||
} |
|||
|
|||
log.info(builder.toString()); |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
|
|||
import static org.junit.Assert.*; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class ClockTest { |
|||
|
|||
@Before |
|||
public void init() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@After |
|||
public void clear() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@Test |
|||
public void defaultClockUseSystemTime() { |
|||
assertFalse(Clock.millis() > System.currentTimeMillis()); |
|||
} |
|||
|
|||
@Test |
|||
public void timeCanBeSet() { |
|||
Clock.setMillis(100L); |
|||
assertEquals(100L, Clock.millis()); |
|||
} |
|||
|
|||
@Test |
|||
public void clockCanBeReseted() { |
|||
Clock.setMillis(100L); |
|||
assertEquals(100L, Clock.millis()); |
|||
Clock.reset(); |
|||
assertFalse(Clock.millis() > System.currentTimeMillis()); |
|||
} |
|||
|
|||
@Test |
|||
public void timeIsShifted() { |
|||
Clock.setMillis(100L); |
|||
Clock.shift(50L); |
|||
assertEquals(150L, Clock.millis()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
import org.junit.Test; |
|||
|
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestLimitPolicyTest { |
|||
|
|||
private HostRequestLimitPolicy limitPolicy = new HostRequestLimitPolicy(10L); |
|||
|
|||
@Test |
|||
public void ifCurrentValueLessThenLimitItIsValid() { |
|||
assertTrue(limitPolicy.isValid(9)); |
|||
} |
|||
|
|||
@Test |
|||
public void ifCurrentValueEqualsToLimitItIsValid() { |
|||
assertTrue(limitPolicy.isValid(10)); |
|||
} |
|||
|
|||
@Test |
|||
public void ifCurrentValueGreaterThenLimitItIsValid() { |
|||
assertFalse(limitPolicy.isValid(11)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
/** |
|||
* 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.transport.quota; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
|||
|
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestsQuotaServiceTest { |
|||
|
|||
private HostRequestsQuotaService quotaService; |
|||
|
|||
private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); |
|||
private HostRequestLimitPolicy requestsPolicy = mock(HostRequestLimitPolicy.class); |
|||
private IntervalRegistryCleaner registryCleaner = mock(IntervalRegistryCleaner.class); |
|||
private IntervalRegistryLogger registryLogger = mock(IntervalRegistryLogger.class); |
|||
|
|||
@Before |
|||
public void init() { |
|||
quotaService = new HostRequestsQuotaService(requestRegistry, requestsPolicy, registryCleaner, registryLogger, true); |
|||
} |
|||
|
|||
@Test |
|||
public void quotaExceededIfRequestCountBiggerThanAllowed() { |
|||
when(requestRegistry.tick("key")).thenReturn(10L); |
|||
when(requestsPolicy.isValid(10L)).thenReturn(false); |
|||
|
|||
assertTrue(quotaService.isQuotaExceeded("key")); |
|||
|
|||
verify(requestRegistry).tick("key"); |
|||
verify(requestsPolicy).isValid(10L); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
|
|||
@Test |
|||
public void quotaNotExceededIfRequestCountLessThanAllowed() { |
|||
when(requestRegistry.tick("key")).thenReturn(10L); |
|||
when(requestsPolicy.isValid(10L)).thenReturn(true); |
|||
|
|||
assertFalse(quotaService.isQuotaExceeded("key")); |
|||
|
|||
verify(requestRegistry).tick("key"); |
|||
verify(requestsPolicy).isValid(10L); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
|
|||
@Test |
|||
public void serviceCanBeDisabled() { |
|||
quotaService = new HostRequestsQuotaService(requestRegistry, requestsPolicy, registryCleaner, registryLogger, false); |
|||
assertFalse(quotaService.isQuotaExceeded("key")); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.Sets; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
|
|||
import java.util.Collections; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestIntervalRegistryTest { |
|||
|
|||
private HostRequestIntervalRegistry registry; |
|||
|
|||
@Before |
|||
public void init() { |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"g1,g2", "b1"); |
|||
} |
|||
|
|||
@Test |
|||
public void newHostCreateNewInterval() { |
|||
assertEquals(1L, registry.tick("host1")); |
|||
} |
|||
|
|||
@Test |
|||
public void existingHostUpdated() { |
|||
registry.tick("aaa"); |
|||
assertEquals(1L, registry.tick("bbb")); |
|||
assertEquals(2L, registry.tick("aaa")); |
|||
} |
|||
|
|||
@Test |
|||
public void expiredIntervalsCleaned() throws InterruptedException { |
|||
registry.tick("aaa"); |
|||
Thread.sleep(150L); |
|||
registry.tick("bbb"); |
|||
registry.clean(); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
assertEquals(2L, registry.tick("bbb")); |
|||
} |
|||
|
|||
@Test |
|||
public void domainFromWhitelistNotCounted(){ |
|||
assertEquals(0L, registry.tick("g1")); |
|||
assertEquals(0L, registry.tick("g1")); |
|||
assertEquals(0L, registry.tick("g2")); |
|||
} |
|||
|
|||
@Test |
|||
public void domainFromBlackListReturnMaxValue(){ |
|||
assertEquals(Long.MAX_VALUE, registry.tick("b1")); |
|||
assertEquals(Long.MAX_VALUE, registry.tick("b1")); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyWhitelistParsedOk(){ |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"", "b1"); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyBlacklistParsedOk(){ |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"", ""); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.Clock; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalCountTest { |
|||
|
|||
@Before |
|||
public void init() { |
|||
Clock.setMillis(1000L); |
|||
} |
|||
|
|||
@After |
|||
public void clear() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@Test |
|||
public void ticksInSameIntervalAreSummed() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(100); |
|||
assertEquals(2L, intervalCount.resetIfExpiredAndTick()); |
|||
} |
|||
|
|||
@Test |
|||
public void oldDataCleanedWhenIntervalExpired() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(101); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
} |
|||
|
|||
@Test |
|||
public void silenceDurationCalculatedFromLastTick() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(10L); |
|||
assertEquals(10L, intervalCount.silenceDuration()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
/** |
|||
* 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.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.ImmutableMap; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.Map; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.mockito.Mockito.mock; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalRegistryLoggerTest { |
|||
|
|||
private IntervalRegistryLogger logger; |
|||
|
|||
private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); |
|||
|
|||
@Before |
|||
public void init() { |
|||
logger = new IntervalRegistryLogger(3, 10, requestRegistry); |
|||
} |
|||
|
|||
@Test |
|||
public void onlyMaxHostsCollected() { |
|||
Map<String, Long> map = ImmutableMap.of("a", 8L, "b", 3L, "c", 1L, "d", 3L); |
|||
Map<String, Long> actual = logger.getTopElements(map); |
|||
Map<String, Long> expected = ImmutableMap.of("a", 8L, "b", 3L, "d", 3L); |
|||
|
|||
assertEquals(expected, actual); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyMapProcessedCorrectly() { |
|||
Map<String, Long> map = Collections.emptyMap(); |
|||
Map<String, Long> actual = logger.getTopElements(map); |
|||
Map<String, Long> expected = Collections.emptyMap(); |
|||
|
|||
assertEquals(expected, actual); |
|||
} |
|||
|
|||
} |
|||
@ -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<String, String> redisTemplate(RedisConnectionFactory cf) { |
|||
RedisTemplate<String, String> 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(); |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -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: |
|||
Loading…
Reference in new issue