committed by
GitHub
29 changed files with 336 additions and 231 deletions
@ -0,0 +1,116 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 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.service.notification.rule.cache; |
||||
|
|
||||
|
import com.github.benmanes.caffeine.cache.Cache; |
||||
|
import com.github.benmanes.caffeine.cache.Caffeine; |
||||
|
import lombok.Data; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.context.event.EventListener; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.notification.rule.NotificationRule; |
||||
|
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
||||
|
import org.thingsboard.server.dao.notification.NotificationRuleService; |
||||
|
|
||||
|
import javax.annotation.PostConstruct; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.concurrent.locks.ReadWriteLock; |
||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class DefaultNotificationRulesCache implements NotificationRulesCache { |
||||
|
|
||||
|
private final NotificationRuleService notificationRuleService; |
||||
|
|
||||
|
@Value("${cache.notificationRules.maxSize:1000}") |
||||
|
private int cacheMaxSize; |
||||
|
@Value("${cache.notificationRules.timeToLiveInMinutes:30}") |
||||
|
private int cacheValueTtl; |
||||
|
private Cache<CacheKey, List<NotificationRule>> cache; |
||||
|
|
||||
|
private final ReadWriteLock lock = new ReentrantReadWriteLock(); |
||||
|
|
||||
|
@PostConstruct |
||||
|
private void init() { |
||||
|
cache = Caffeine.newBuilder() |
||||
|
.maximumSize(cacheMaxSize) |
||||
|
.expireAfterAccess(cacheValueTtl, TimeUnit.MINUTES) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
@EventListener(ComponentLifecycleMsg.class) |
||||
|
public void onComponentLifecycleEvent(ComponentLifecycleMsg event) { |
||||
|
switch (event.getEntityId().getEntityType()) { |
||||
|
case NOTIFICATION_RULE: |
||||
|
evict(event.getTenantId()); // TODO: evict by trigger type of the rule
|
||||
|
break; |
||||
|
case TENANT: |
||||
|
if (event.getEvent() == ComponentLifecycleEvent.DELETED) { |
||||
|
lock.writeLock().lock(); // locking in case rules for tenant are fetched while evicting
|
||||
|
try { |
||||
|
evict(event.getTenantId()); |
||||
|
} finally { |
||||
|
lock.writeLock().unlock(); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<NotificationRule> get(TenantId tenantId, NotificationRuleTriggerType triggerType) { |
||||
|
lock.readLock().lock(); |
||||
|
try { |
||||
|
log.trace("Retrieving notification rules of type {} for tenant {} from cache", triggerType, tenantId); |
||||
|
return cache.get(key(tenantId, triggerType), k -> { |
||||
|
List<NotificationRule> rules = notificationRuleService.findNotificationRulesByTenantIdAndTriggerType(tenantId, triggerType); |
||||
|
log.trace("Fetched notification rules of type {} for tenant {} (count: {})", triggerType, tenantId, rules.size()); |
||||
|
return !rules.isEmpty() ? rules : Collections.emptyList(); |
||||
|
}); |
||||
|
} finally { |
||||
|
lock.readLock().unlock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void evict(TenantId tenantId) { |
||||
|
cache.invalidateAll(Arrays.stream(NotificationRuleTriggerType.values()) |
||||
|
.map(triggerType -> key(tenantId, triggerType)) |
||||
|
.collect(Collectors.toList())); |
||||
|
log.trace("Evicted all notification rules for tenant {} from cache", tenantId); |
||||
|
} |
||||
|
|
||||
|
private static CacheKey key(TenantId tenantId, NotificationRuleTriggerType triggerType) { |
||||
|
return new CacheKey(tenantId, triggerType); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
private static class CacheKey { |
||||
|
private final TenantId tenantId; |
||||
|
private final NotificationRuleTriggerType triggerType; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,43 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2023 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.notification.cache; |
|
||||
|
|
||||
import lombok.AllArgsConstructor; |
|
||||
import lombok.Builder; |
|
||||
import lombok.Data; |
|
||||
import lombok.NoArgsConstructor; |
|
||||
import org.thingsboard.server.common.data.id.TenantId; |
|
||||
import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; |
|
||||
|
|
||||
import java.io.Serializable; |
|
||||
|
|
||||
@Data |
|
||||
@AllArgsConstructor |
|
||||
@NoArgsConstructor |
|
||||
@Builder |
|
||||
public class NotificationRuleCacheKey implements Serializable { |
|
||||
|
|
||||
private static final long serialVersionUID = 5987113265482170L; |
|
||||
|
|
||||
private TenantId tenantId; |
|
||||
private NotificationRuleTriggerType triggerType; |
|
||||
|
|
||||
@Override |
|
||||
public String toString() { |
|
||||
return tenantId + "_" + triggerType; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2023 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.notification.cache; |
|
||||
|
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|
||||
import org.springframework.cache.CacheManager; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.cache.CaffeineTbTransactionalCache; |
|
||||
import org.thingsboard.server.common.data.CacheConstants; |
|
||||
|
|
||||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) |
|
||||
@Service |
|
||||
public class NotificationRuleCaffeineCache extends CaffeineTbTransactionalCache<NotificationRuleCacheKey, NotificationRuleCacheValue> { |
|
||||
|
|
||||
public NotificationRuleCaffeineCache(CacheManager cacheManager) { |
|
||||
super(cacheManager, CacheConstants.NOTIFICATION_RULES_CACHE); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -1,35 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2023 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.notification.cache; |
|
||||
|
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.cache.CacheSpecsMap; |
|
||||
import org.thingsboard.server.cache.RedisTbTransactionalCache; |
|
||||
import org.thingsboard.server.cache.TBRedisCacheConfiguration; |
|
||||
import org.thingsboard.server.cache.TbFSTRedisSerializer; |
|
||||
import org.thingsboard.server.common.data.CacheConstants; |
|
||||
|
|
||||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") |
|
||||
@Service |
|
||||
public class NotificationRuleRedisCache extends RedisTbTransactionalCache<NotificationRuleCacheKey, NotificationRuleCacheValue> { |
|
||||
|
|
||||
public NotificationRuleRedisCache(CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration) { |
|
||||
super(CacheConstants.NOTIFICATION_RULES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,46 @@ |
|||||
|
#### New platform version notification templatization |
||||
|
|
||||
|
<div class="divider"></div> |
||||
|
<br/> |
||||
|
|
||||
|
Notification subject and message fields support templatization. The list of available templatization parameters depends on the template type. |
||||
|
See the available types and parameters below: |
||||
|
|
||||
|
Available template parameters: |
||||
|
|
||||
|
* *recipientEmail* - email of the recipient; |
||||
|
* *recipientFirstName* - first name of the recipient; |
||||
|
* *recipientLastName* - last name of the recipient; |
||||
|
* *latestVersion* - the latest platform version available; |
||||
|
* *latestVersionReleaseNotesUrl* - release notes link for latest version; |
||||
|
* *upgradeInstructionsUrl* - upgrade instructions link for latest version; |
||||
|
* *currentVersion* - the current platform version |
||||
|
* *currentVersionReleaseNotesUrl* - release notes link for current version; |
||||
|
|
||||
|
Parameter names must be wrapped using `${...}`. For example: `${recipientFirstName}`. |
||||
|
You may also modify the value of the parameter with one of the suffixes: |
||||
|
|
||||
|
* `upperCase`, for example - `${recipientFirstName:upperCase}` |
||||
|
* `lowerCase`, for example - `${recipientFirstName:lowerCase}` |
||||
|
* `capitalize`, for example - `${recipientFirstName:capitalize}` |
||||
|
|
||||
|
<div class="divider"></div> |
||||
|
|
||||
|
##### Examples |
||||
|
|
||||
|
Let's assume that new 3.5.0 version is released but currently deployed version is 3.4.4. The following template: |
||||
|
|
||||
|
```text |
||||
|
New version ${latestVersion} is available. Current version is ${currentVersion} |
||||
|
{:copy-code} |
||||
|
``` |
||||
|
|
||||
|
will be transformed to: |
||||
|
|
||||
|
```text |
||||
|
New version 3.5.0 is available. Current version is 3.4.4 |
||||
|
{:copy-code} |
||||
|
``` |
||||
|
|
||||
|
<br> |
||||
|
<br> |
||||
Loading…
Reference in new issue