diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 0f40dd37f5..aa1f8bc909 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -118,17 +118,23 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { this.rpcSubscriptions = new HashMap<>(); this.toDeviceRpcPendingMap = new HashMap<>(); this.toServerRpcPendingMap = new HashMap<>(); - initAttributes(); - restoreSessions(); + if (initAttributes()) { + restoreSessions(); + } } - private void initAttributes() { + private boolean initAttributes() { Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId); - this.deviceName = device.getName(); - this.deviceType = device.getType(); - this.defaultMetaData = new TbMsgMetaData(); - this.defaultMetaData.putValue("deviceName", deviceName); - this.defaultMetaData.putValue("deviceType", deviceType); + if (device != null) { + this.deviceName = device.getName(); + this.deviceType = device.getType(); + this.defaultMetaData = new TbMsgMetaData(); + this.defaultMetaData.putValue("deviceName", deviceName); + this.defaultMetaData.putValue("deviceType", deviceType); + return true; + } else { + return false; + } } void processRpcRequest(ActorContext context, ToDeviceRpcRequestActorMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 368634eb06..44b6f3b6c5 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -91,17 +91,19 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - // Creating and starting the actors; - for (RuleNode ruleNode : ruleNodeList) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + // Creating and starting the actors; + for (RuleNode ruleNode : ruleNodeList) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } + initRoutes(ruleChain, ruleNodeList); + started = true; } - initRoutes(ruleChain, ruleNodeList); - started = true; } else { onUpdate(context); } @@ -110,31 +112,33 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor ruleNodeList = service.getRuleChainNodes(tenantId, entityId); - log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); - for (RuleNode ruleNode : ruleNodeList) { - RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); - if (existing == null) { - log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); - nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); - } else { - log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); - existing.setSelf(ruleNode); - existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + if (ruleChain != null) { + ruleChainName = ruleChain.getName(); + List ruleNodeList = service.getRuleChainNodes(tenantId, entityId); + log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); + for (RuleNode ruleNode : ruleNodeList) { + RuleNodeCtx existing = nodeActors.get(ruleNode.getId()); + if (existing == null) { + log.trace("[{}][{}] Creating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + ActorRef ruleNodeActor = createRuleNodeActor(context, ruleNode); + nodeActors.put(ruleNode.getId(), new RuleNodeCtx(tenantId, self, ruleNodeActor, ruleNode)); + } else { + log.trace("[{}][{}] Updating rule node [{}]: {}", entityId, ruleNode.getId(), ruleNode.getName(), ruleNode); + existing.setSelf(ruleNode); + existing.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, existing.getSelf().getId(), ComponentLifecycleEvent.UPDATED), self); + } } + + Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + removedRules.forEach(ruleNodeId -> { + log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); + RuleNodeCtx removed = nodeActors.remove(ruleNodeId); + removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); + }); + + initRoutes(ruleChain, ruleNodeList); } - - Set existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); - List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); - removedRules.forEach(ruleNodeId -> { - log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); - RuleNodeCtx removed = nodeActors.remove(ruleNodeId); - removed.getSelfActor().tell(new ComponentLifecycleMsg(tenantId, removed.getSelf().getId(), ComponentLifecycleEvent.DELETED), self); - }); - - initRoutes(ruleChain, ruleNodeList); } @Override diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java index fc8ff3dc0a..a6543795ac 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -55,7 +55,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor componentClazz = Class.forName(ruleNode.getType()); - TbNode tbNode = (TbNode) (componentClazz.newInstance()); - tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + TbNode tbNode = null; + if (ruleNode != null) { + Class componentClazz = Class.forName(ruleNode.getType()); + tbNode = (TbNode) (componentClazz.newInstance()); + tbNode.init(defaultCtx, new TbNodeConfiguration(ruleNode.getConfiguration())); + } return tbNode; } diff --git a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java index c932f2133e..34c4e3c46d 100644 --- a/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java +++ b/application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; @Configuration -@ConfigurationProperties(prefix = "audit_log.logging_level") +@ConfigurationProperties(prefix = "audit-log.logging-level") public class AuditLogLevelProperties { private Map mask = new HashMap<>(); diff --git a/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java new file mode 100644 index 0000000000..b70f781acd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2019 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.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +@Configuration +@EnableScheduling +public class SchedulingConfiguration implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + taskRegistrar.setScheduler(taskScheduler()); + } + + @Bean(destroyMethod="shutdown") + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler(); + threadPoolScheduler.setThreadNamePrefix("TB-Scheduling-"); + threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); + threadPoolScheduler.setRemoveOnCancelPolicy(true); + return threadPoolScheduler; + } +} diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java index 2402a2f6a9..fc25af3a56 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java @@ -15,11 +15,28 @@ */ package org.thingsboard.server.config; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.ExtendedProperties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; @Configuration public class ThingsboardMessageConfiguration { @@ -32,5 +49,114 @@ public class ThingsboardMessageConfiguration { messageSource.setDefaultEncoding("UTF-8"); return messageSource; } - + + private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/"; + + private ResourceLoader resourceLoader = new DefaultResourceLoader(); + + @Bean + public VelocityEngine velocityEngine() { + VelocityEngine velocityEngine = new VelocityEngine(); + try { + Resource resource = resourceLoader.getResource(DEFAULT_RESOURCE_LOADER_PATH); + File file = resource.getFile(); + velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, file.getAbsolutePath()); + } catch (IOException e) { + initSpringResourceLoader(velocityEngine, DEFAULT_RESOURCE_LOADER_PATH); + } + velocityEngine.init(); + return velocityEngine; + } + + private void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) { + velocityEngine.setProperty( + RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName()); + velocityEngine.setProperty( + SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true"); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER, resourceLoader); + velocityEngine.setApplicationAttribute( + SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath); + } + + @Slf4j + static class SpringResourceLoader extends org.apache.velocity.runtime.resource.loader.ResourceLoader { + + public static final String NAME = "spring"; + + public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class"; + + public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache"; + + public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader"; + + public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path"; + + private org.springframework.core.io.ResourceLoader resourceLoader; + + private String[] resourceLoaderPaths; + + + @Override + public void init(ExtendedProperties configuration) { + this.resourceLoader = (org.springframework.core.io.ResourceLoader) + this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER); + String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH); + if (this.resourceLoader == null) { + throw new IllegalArgumentException( + "'resourceLoader' application attribute must be present for SpringResourceLoader"); + } + if (resourceLoaderPath == null) { + throw new IllegalArgumentException( + "'resourceLoaderPath' application attribute must be present for SpringResourceLoader"); + } + this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath); + for (int i = 0; i < this.resourceLoaderPaths.length; i++) { + String path = this.resourceLoaderPaths[i]; + if (!path.endsWith("/")) { + this.resourceLoaderPaths[i] = path + "/"; + } + } + if (log.isInfoEnabled()) { + log.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader + + "] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths)); + } + } + + @Override + public InputStream getResourceStream(String source) throws ResourceNotFoundException { + if (log.isDebugEnabled()) { + log.debug("Looking for Velocity resource with name [" + source + "]"); + } + for (String resourceLoaderPath : this.resourceLoaderPaths) { + org.springframework.core.io.Resource resource = + this.resourceLoader.getResource(resourceLoaderPath + source); + try { + return resource.getInputStream(); + } + catch (IOException ex) { + if (log.isDebugEnabled()) { + log.debug("Could not find Velocity resource: " + resource); + } + } + } + throw new ResourceNotFoundException( + "Could not find resource [" + source + "] in Spring resource loader path"); + } + + @Override + public boolean isSourceModified(org.apache.velocity.runtime.resource.Resource resource) { + return false; + } + + @Override + public long getLastModified(org.apache.velocity.runtime.resource.Resource resource) { + return 0; + } + + } } diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 541e4bd17d..653ad68232 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -57,7 +57,7 @@ import java.util.List; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) -@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) +@Order(SecurityProperties.BASIC_AUTH_ORDER) public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 711d4ce029..b8b703e4fc 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -58,7 +58,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Map attributes) throws Exception { + Map attributes) throws Exception { SecurityUser user = null; try { user = getCurrentUser(); @@ -73,7 +73,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer { @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Exception exception) { + Exception exception) { //Do nothing } }); diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index f8f02a9d3b..169bba8895 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -392,4 +392,4 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr } } -} +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java index f6f43768a0..a0cedcf14f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; import org.thingsboard.server.service.install.cql.CQLStatementsParser; @@ -30,6 +31,7 @@ public abstract class CassandraAbstractDatabaseSchemaService implements Database private static final String CASSANDRA_DIR = "cassandra"; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster cluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index 13c198e6e5..1bab31d9e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.install; import com.datastax.driver.core.KeyspaceMetadata; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -65,6 +66,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { private CassandraCluster cluster; @Autowired + @Qualifier("CassandraInstallCluster") private CassandraInstallCluster installCluster; @Autowired diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 7ade02bf19..217d62b54e 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -18,7 +18,9 @@ package org.thingsboard.server.service.mail; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.exception.VelocityException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.MessageSource; @@ -26,7 +28,6 @@ import org.springframework.core.NestedRuntimeException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; -import org.springframework.ui.velocity.VelocityEngineUtils; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -39,6 +40,8 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import javax.annotation.PostConstruct; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import java.io.StringWriter; +import java.io.Writer; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -126,7 +129,7 @@ public class DefaultMailService implements MailService { Map model = new HashMap(); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "test.vm", UTF_8, model); sendMail(testMailSender, mailFrom, email, subject, message); @@ -141,7 +144,7 @@ public class DefaultMailService implements MailService { model.put("activationLink", activationLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "activation.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -156,7 +159,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "account.activated.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -171,7 +174,7 @@ public class DefaultMailService implements MailService { model.put("passwordResetLink", passwordResetLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "reset.password.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -186,7 +189,7 @@ public class DefaultMailService implements MailService { model.put("loginLink", loginLink); model.put(TARGET_EMAIL, email); - String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, + String message = mergeTemplateIntoString(this.engine, "password.was.reset.vm", UTF_8, model); sendMail(mailSender, mailFrom, email, subject, message); @@ -225,6 +228,22 @@ public class DefaultMailService implements MailService { } } + private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation, + String encoding, Map model) throws VelocityException { + + StringWriter result = new StringWriter(); + mergeTemplate(velocityEngine, templateLocation, encoding, model, result); + return result.toString(); + } + + private static void mergeTemplate( + VelocityEngine velocityEngine, String templateLocation, String encoding, + Map model, Writer writer) throws VelocityException { + + VelocityContext velocityContext = new VelocityContext(model); + velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer); + } + protected ThingsboardException handleException(Exception exception) { String message; if (exception instanceof NestedRuntimeException) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java index e6b28b33cd..86747150c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java @@ -27,7 +27,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketSession; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 235646f6c2..72b8159407 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -293,11 +293,13 @@ spring.mvc.cors: # spring serve gzip compressed static resources spring.resources.chain: - gzipped: "true" + compressed: "true" strategy: content: enabled: "true" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" + # HSQLDB DAO Configuration spring: data: @@ -331,7 +333,7 @@ spring: # password: "${SPRING_DATASOURCE_PASSWORD:postgres}" # Audit log parameters -audit_log: +audit-log: # Enable/disable audit log functionality. enabled: "${AUDIT_LOG_ENABLED:true}" # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS @@ -340,7 +342,7 @@ audit_log: default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}" # Logging levels per each entity type. # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) - logging_level: + logging-level: mask: "device": "${AUDIT_LOG_MASK_DEVICE:W}" "asset": "${AUDIT_LOG_MASK_ASSET:W}" diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index 218d2e64aa..67efdcdb8b 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -128,7 +128,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), @@ -183,7 +183,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1},\"timeout\": 6000}"; String deviceId = savedDevice.getId().getId().toString(); doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java index 8e8639c50c..50de5727f9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java @@ -111,7 +111,7 @@ public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractContr client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); String payload = "{\"key\":\"value\"}"; String result = doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); - latch.await(3, TimeUnit.SECONDS); + latch.await(10, TimeUnit.SECONDS); assertEquals(payload, callback.getPayload()); assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 519607b840..d1395f43db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,11 +18,7 @@ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.model.ToData; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; public abstract class DaoUtil { @@ -50,6 +46,14 @@ public abstract class DaoUtil { return object; } + public static T getData(Optional> data) { + T object = null; + if (data.isPresent()) { + object = data.get().toData(); + } + return object; + } + public static UUID getId(UUIDBased idBased) { UUID id = null; if (idBased != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index c5518bef9d..ad3db8781f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -60,7 +60,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Slf4j @Service -@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "true") +@ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true") public class AuditLogServiceImpl implements AuditLogService { private static final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java index 860e0a962e..6ffaf1ea00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java @@ -88,11 +88,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao redisTemplate(RedisConnectionFactory cf) { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(cf); - return redisTemplate; - } - - @Bean - public CacheManager cacheManager(RedisTemplate redisTemplate) { - return new RedisCacheManager(redisTemplate); + public CacheManager cacheManager(RedisConnectionFactory cf) { + DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService(); + RedisCacheConfiguration.registerDefaultConverters(redisConversionService); + registerDefaultConverters(redisConversionService); + RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService); + return RedisCacheManager.builder(cf).cacheDefaults(configuration).build(); } @Bean @@ -73,5 +93,8 @@ public class TBRedisCacheConfiguration { return new PreviousDeviceCredentialsIdKeyGenerator(); } - + private static void registerDefaultConverters(ConverterRegistry registry) { + Assert.notNull(registry, "ConverterRegistry must not be null!"); + registry.addConverter(EntityId.class, String.class, EntityId::toString); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java index 84073f62cd..4fa383656b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraCluster") @NoSqlAnyDao public class CassandraCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java index 54365e5281..2492552c99 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java +++ b/dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java @@ -21,7 +21,7 @@ import org.thingsboard.server.dao.util.NoSqlAnyDao; import javax.annotation.PostConstruct; -@Component +@Component("CassandraInstallCluster") @NoSqlAnyDao @Profile("install") public class CassandraInstallCluster extends AbstractCassandraCluster { diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java index b0840140e3..09d4e53928 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java @@ -27,6 +27,7 @@ import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.exceptions.CodecNotFoundException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.model.type.AuthorityCodec; @@ -44,6 +45,7 @@ import java.util.concurrent.ConcurrentMap; public abstract class CassandraAbstractDao { @Autowired + @Qualifier("CassandraCluster") protected CassandraCluster cluster; private ConcurrentMap preparedStatementMap = new ConcurrentHashMap<>(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 3b0bec9064..34c0a7cb25 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -27,6 +27,7 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseEntity; import java.util.List; +import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -67,23 +68,23 @@ public abstract class JpaAbstractDao, D> @Override public D findById(TenantId tenantId, UUID key) { log.debug("Get entity by key {}", key); - E entity = getCrudRepository().findOne(fromTimeUUID(key)); + Optional entity = getCrudRepository().findById(fromTimeUUID(key)); return DaoUtil.getData(entity); } @Override public ListenableFuture findByIdAsync(TenantId tenantId, UUID key) { log.debug("Get entity by key async {}", key); - return service.submit(() -> DaoUtil.getData(getCrudRepository().findOne(fromTimeUUID(key)))); + return service.submit(() -> DaoUtil.getData(getCrudRepository().findById(fromTimeUUID(key)))); } @Override @Transactional public boolean removeById(TenantId tenantId, UUID id) { String key = fromTimeUUID(id); - getCrudRepository().delete(key); + getCrudRepository().deleteById(key); log.debug("Remove request: {}", key); - return getCrudRepository().findOne(key) == null; + return !getCrudRepository().existsById(key); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index c2cfee19d3..dc65018cf1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -52,7 +52,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl AttributeKvCompositeKey compositeKey = getAttributeKvCompositeKey(entityId, attributeType, attributeKey); return Futures.immediateFuture( - Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findOne(compositeKey)))); + Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findById(compositeKey)))); } @Override @@ -64,7 +64,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl getAttributeKvCompositeKey(entityId, attributeType, attributeKey)) .collect(Collectors.toList()); return Futures.immediateFuture( - DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAll(compositeKeys)))); + DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAllById(compositeKeys)))); } @Override @@ -103,7 +103,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl }).collect(Collectors.toList()); return service.submit(() -> { - attributeKvRepository.delete(entitiesToDelete); + attributeKvRepository.deleteAll(entitiesToDelete); return null; }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index 1cc7104365..c66ea9c5cc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -66,7 +66,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> relationRepository.findOne(key) != null); + return service.submit(() -> relationRepository.existsById(key)); } @Override public ListenableFuture getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); - return service.submit(() -> DaoUtil.getData(relationRepository.findOne(key))); + return service.submit(() -> DaoUtil.getData(relationRepository.findById(key))); } private RelationCompositeKey getRelationCompositeKey(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { @@ -152,9 +152,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple } private boolean deleteRelationIfExists(RelationCompositeKey key) { - boolean relationExistsBeforeDelete = relationRepository.exists(key); + boolean relationExistsBeforeDelete = relationRepository.existsById(key); if (relationExistsBeforeDelete) { - relationRepository.delete(key); + relationRepository.deleteById(key); } return relationExistsBeforeDelete; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java index 208b63a0b0..e2fb1ff213 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java @@ -53,7 +53,7 @@ public interface RelationRepository RelationEntity save(RelationEntity entity); @Transactional - void delete(RelationCompositeKey id); + void deleteById(RelationCompositeKey id); @Transactional void deleteByFromIdAndFromType(String fromId, String fromType); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 92e5c1db0d..2b36eb80b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -284,10 +284,10 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp entityId.getEntityType(), fromTimeUUID(entityId.getId()), key); - TsKvLatestEntity entry = tsKvLatestRepository.findOne(compositeKey); + Optional entry = tsKvLatestRepository.findById(compositeKey); TsKvEntry result; - if (entry != null) { - result = DaoUtil.getData(entry); + if (entry.isPresent()) { + result = DaoUtil.getData(entry.get()); } else { result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java index b57cd33c6c..c74dbd54e5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java +++ b/dao/src/test/java/org/thingsboard/server/dao/CustomCassandraCQLUnit.java @@ -82,6 +82,7 @@ public class CustomCassandraCQLUnit extends BaseCassandraUnit { session = null; cluster = null; } + System.setSecurityManager(null); } // Getters for those who do not like to directly access fields diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 7440a966fe..d8f18e090f 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -4,10 +4,10 @@ zk.zk_dir=/thingsboard updates.enabled=false -audit_log.enabled=true -audit_log.by_tenant_partitioning=MONTHS -audit_log.default_query_period=30 -audit_log.sink.type=none +audit-log.enabled=true +audit-log.by_tenant_partitioning=MONTHS +audit-log.default_query_period=30 +audit-log.sink.type=none cache.type=caffeine #cache.type=redis diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 3357425fce..745aa9e1e0 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -4,6 +4,7 @@ database.entities.type=sql sql.ts_inserts_executor_type=fixed sql.ts_inserts_fixed_thread_pool_size=10 +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=validate spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect diff --git a/pom.xml b/pom.xml index f2cde239b7..7f026e3943 100755 --- a/pom.xml +++ b/pom.xml @@ -29,10 +29,10 @@ ${basedir} - 1.4.3.RELEASE - 4.3.4.RELEASE - 4.2.0.RELEASE - 1.8.10.RELEASE + 2.1.3.RELEASE + 5.1.5.RELEASE + 5.1.4.RELEASE + 2.1.5.RELEASE 2.9.0 0.7.0 2.2.0 @@ -41,8 +41,8 @@ 1.2.3 1.9.5 0.10 - 3.5.0 - 3.3.0.2 + 3.6.0 + 3.5.0.1 1.2.7 21.0 2.6.1 @@ -50,7 +50,7 @@ 1.5.0 2.5 1.4 - 2.8.11.1 + 2.9.7 2.2.6 2.11 2.4.2 @@ -60,11 +60,11 @@ 2.0 1.4.3 4.0.1 - 3.0.2 - 1.12.0 + 3.6.1 + 1.16.1 1.16.18 1.1.0 - 4.1.22.Final + 4.1.30.Final 1.5.0 4.8.0 2.19.1 @@ -85,6 +85,8 @@ 2.0.0 4.1.1 2.57 + 2.7.7 + 1.23 @@ -512,6 +514,16 @@ + + org.yaml + snakeyaml + ${snakeyaml.version} + + + antlr + antlr + ${antlr.version} + com.rabbitmq amqp-client @@ -794,6 +806,12 @@ de.ruedigermoeller fst ${fst.version} + + + com.fasterxml.jackson.core + jackson-core + + io.springfox.ui