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