diff --git a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java index 5d1262f233..e44c28dc39 100644 --- a/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java @@ -52,6 +52,8 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED; @@ -96,11 +98,13 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi @Lazy private ClusterRoutingService routingService; + private ExecutorService reconnectExecutorService; + private CuratorFramework client; private PathChildrenCache cache; private String nodePath; - private volatile boolean stopped = false; + private volatile boolean stopped = true; @PostConstruct public void init() { @@ -110,9 +114,15 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms")); Assert.notNull(zkSessionTimeout, MiscUtils.missingProperty("zk.session_timeout_ms")); + reconnectExecutorService = Executors.newSingleThreadExecutor(); + log.info("Initializing discovery service using ZK connect string: {}", zkUrl); zkNodesDir = zkDir + "/nodes"; + initZkClient(); + } + + private void initZkClient() { try { client = CuratorFrameworkFactory.newClient(zkUrl, zkSessionTimeout, zkConnectionTimeout, new RetryForever(zkRetryInterval)); client.start(); @@ -120,6 +130,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi cache = new PathChildrenCache(client, zkNodesDir, true); cache.getListenable().addListener(this); cache.start(); + stopped = false; + log.info("ZK client connected"); } catch (Exception e) { log.error("Failed to connect to ZK: {}", e.getMessage(), e); CloseableUtils.closeQuietly(cache); @@ -128,12 +140,20 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi } } - @PreDestroy - public void destroy() { + private void destroyZkClient() { stopped = true; - unpublishCurrentServer(); + try { + unpublishCurrentServer(); + } catch (Exception e) {} CloseableUtils.closeQuietly(cache); CloseableUtils.closeQuietly(client); + log.info("ZK client disconnected"); + } + + @PreDestroy + public void destroy() { + destroyZkClient(); + reconnectExecutorService.shutdownNow(); log.info("Stopped discovery service"); } @@ -180,20 +200,21 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi return (client, newState) -> { log.info("[{}:{}] ZK state changed: {}", self.getHost(), self.getPort(), newState); if (newState == ConnectionState.LOST) { - reconnect(); + reconnectExecutorService.submit(this::reconnect); } }; } - private boolean reconnectInProgress = false; + private volatile boolean reconnectInProgress = false; private synchronized void reconnect() { if (!reconnectInProgress) { reconnectInProgress = true; try { - client.blockUntilConnected(); + destroyZkClient(); + initZkClient(); publishCurrentServer(); - } catch (InterruptedException e) { + } catch (Exception e) { log.error("Failed to reconnect to ZK: {}", e.getMessage(), e); } finally { reconnectInProgress = false; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 72b8159407..1069f9d6e6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,23 +273,17 @@ updates: spring.mvc.cors: mappings: # Intercept path - "/api/auth/**": + "[/api/**]": #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. allowed-origins: "*" #Comma-separated list of methods to allow. '*' allows all methods. - allowed-methods: "POST,GET,OPTIONS" + allowed-methods: "*" #Comma-separated list of headers to allow in a request. '*' allows all headers. allowed-headers: "*" #How long, in seconds, the response from a pre-flight request can be cached by clients. max-age: "1800" #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" - "/api/v1/**": - allowed-origins: "*" - allowed-methods: "*" - allowed-headers: "*" - max-age: "1800" - allow-credentials: "true" # spring serve gzip compressed static resources spring.resources.chain: diff --git a/pom.xml b/pom.xml index b0bf2a51c3..59cc98aef0 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.9.7 + 2.9.8 2.2.6 2.11 2.4.2 @@ -59,7 +59,7 @@ 1.7 2.0 1.4.3 - 4.0.1 + 4.2.0 3.6.1 1.16.1 1.16.18 diff --git a/ui/src/app/components/contact.directive.js b/ui/src/app/components/contact.directive.js index 93912882ed..cab6f52111 100644 --- a/ui/src/app/components/contact.directive.js +++ b/ui/src/app/components/contact.directive.js @@ -283,7 +283,7 @@ function Contact($compile, $templateCache) { "Austria": "[0-9]{4}", "Belgium": "[0-9]{4}", "Brazil": "[0-9]{5}[\\-]?[0-9]{3}", - "Canada": "[A-Za-z][0-9][A-Za-z] [0-9][A-Za-z][0-9]", + "Canada": "^(?!.*[DFIOQU])[A-VXY][0-9][A-Z][ -]?[0-9][A-Z][0-9]$", "Denmark": "[0-9]{3,4}", "Faroe Islands": "[0-9]{3,4}", "Netherlands": "[1-9][0-9]{3}\\s?[a-zA-Z]{2}", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index dfef502f09..d1fe2c88c9 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1295,7 +1295,8 @@ "metadata-required": "Metadata entries can't be empty.", "output": "Output", "test": "Test", - "help": "Help" + "help": "Help", + "reset-debug-mode": "Reset debug mode in all nodes" }, "tenant": { "tenant": "Tenant", diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 3ad4371cc1..2d05051de8 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -1288,7 +1288,8 @@ "metadata-required": "Метаданные объекта не могут быть пустыми.", "output": "Выход", "test": "Протестировать", - "help": "Помощь" + "help": "Помощь", + "reset-debug-mode": "Сбросить режим отладки во всех правилах" }, "tenant": { "tenant": "Владелец", diff --git a/ui/src/app/rulechain/rulechain.controller.js b/ui/src/app/rulechain/rulechain.controller.js index 3744b63198..195dcffef5 100644 --- a/ui/src/app/rulechain/rulechain.controller.js +++ b/ui/src/app/rulechain/rulechain.controller.js @@ -108,6 +108,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.objectsSelected = objectsSelected; vm.deleteSelected = deleteSelected; + vm.isDebugModeEnabled = isDebugModeEnabled; + vm.resetDebugModeInAllNodes = resetDebugModeInAllNodes; + vm.triggerResize = triggerResize; vm.openRuleChainContextMenu = openRuleChainContextMenu; @@ -1342,6 +1345,19 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time vm.modelservice.deleteSelected(); } + function isDebugModeEnabled() { + var res = $filter('filter')(vm.ruleChainModel.nodes, {debugMode: true}); + return (res && res.length); + } + + function resetDebugModeInAllNodes() { + vm.ruleChainModel.nodes.forEach((node) => { + if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) { + node.debugMode = false; + } + }); + } + function triggerResize() { var w = angular.element($window); w.triggerHandler('resize'); diff --git a/ui/src/app/rulechain/rulechain.tpl.html b/ui/src/app/rulechain/rulechain.tpl.html index 80425e59c8..4a20947497 100644 --- a/ui/src/app/rulechain/rulechain.tpl.html +++ b/ui/src/app/rulechain/rulechain.tpl.html @@ -223,6 +223,15 @@ + + + {{ 'rulenode.reset-debug-mode' | translate }} + + + { + return seriesHover.index === seriesIndex; + }); + if (found && found.length) { + content += seriesInfoDivFromInfo(found[0], seriesIndex); } } else { var seriesDiv = $('
'); @@ -161,7 +163,7 @@ export default class TbFlot { if (i == hoverInfo.seriesHover.length) { break; } - seriesHoverInfo = hoverInfo.seriesHover[i]; + var seriesHoverInfo = hoverInfo.seriesHover[i]; columnContent += seriesInfoDivFromInfo(seriesHoverInfo, seriesIndex); } columnDiv.html(columnContent);