Browse Source

Migrate to Spring Boot 2.1.0 and Spring 5.1.2

pull/1500/head
Igor Kulikov 8 years ago
parent
commit
41e2eaea44
  1. 2
      application/pom.xml
  2. 2
      application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java
  3. 47
      application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java
  4. 128
      application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java
  5. 2
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  6. 61
      application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java
  7. 47
      application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
  8. 50
      application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java
  9. 2
      application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
  11. 32
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  12. 48
      application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java
  13. 86
      application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java
  14. 3
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  15. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java
  16. 8
      application/src/main/resources/thingsboard.yml
  17. 4
      application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  18. 14
      dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java
  19. 2
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  20. 4
      dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
  21. 2
      dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java
  22. 2
      dao/src/main/java/org/thingsboard/server/dao/audit/sink/DummyAuditLogSink.java
  23. 16
      dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java
  24. 43
      dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java
  25. 2
      dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java
  26. 2
      dao/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java
  27. 2
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractDao.java
  28. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java
  29. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java
  30. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java
  31. 8
      dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java
  32. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java
  33. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
  34. 8
      dao/src/test/resources/application-test.properties
  35. 1
      dao/src/test/resources/sql-test.properties
  36. 3
      docker/docker-compose.yml
  37. 48
      pom.xml

2
application/pom.xml

@ -122,7 +122,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-webflux</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>

2
application/src/main/java/org/thingsboard/server/config/AuditLogLevelProperties.java

@ -22,7 +22,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Configuration @Configuration
@ConfigurationProperties(prefix = "audit_log.logging_level") @ConfigurationProperties(prefix = "audit-log.logging-level")
public class AuditLogLevelProperties { public class AuditLogLevelProperties {
private Map<String, String> mask = new HashMap<>(); private Map<String, String> mask = new HashMap<>();

47
application/src/main/java/org/thingsboard/server/config/SchedulingConfiguration.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2018 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;
}
}

128
application/src/main/java/org/thingsboard/server/config/ThingsboardMessageConfiguration.java

@ -15,11 +15,28 @@
*/ */
package org.thingsboard.server.config; 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.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.context.support.ResourceBundleMessageSource; 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 @Configuration
public class ThingsboardMessageConfiguration { public class ThingsboardMessageConfiguration {
@ -32,5 +49,114 @@ public class ThingsboardMessageConfiguration {
messageSource.setDefaultEncoding("UTF-8"); messageSource.setDefaultEncoding("UTF-8");
return messageSource; 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;
}
}
} }

2
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -57,7 +57,7 @@ import java.util.List;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true) @EnableGlobalMethodSecurity(prePostEnabled=true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) @Order(SecurityProperties.BASIC_AUTH_ORDER)
public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter { public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization"; public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";

61
application/src/main/java/org/thingsboard/server/config/ThingsboardWebFluxSecurityConfig.java

@ -0,0 +1,61 @@
/**
* Copyright © 2016-2018 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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
//@EnableWebFluxSecurity
//@EnableReactiveMethodSecurity
public class ThingsboardWebFluxSecurityConfig {
private static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
@Autowired
private ReactiveAuthenticationManager webfluxAuthenticationManager;
@Autowired
private ServerSecurityContextRepository jwtTokenSecurityContextRepository;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.cors()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.exceptionHandling()
.and()
.authenticationManager(webfluxAuthenticationManager)
.securityContextRepository(jwtTokenSecurityContextRepository)
.authorizeExchange()
.pathMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT)
.authenticated()
.and()
.build();
}
}

47
application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java

@ -21,38 +21,57 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.socket.WebSocketHandler; import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.socket.server.HandshakeInterceptor; import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; import org.springframework.web.reactive.socket.server.support.HandshakeWebSocketService;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
import org.springframework.web.server.ServerWebExchange;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.controller.plugin.TbWebSocketHandler;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Configuration @Configuration
@EnableWebSocket public class WebSocketConfiguration {
public class WebSocketConfiguration implements WebSocketConfigurer {
public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/";
public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER"; public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER";
private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**";
@Bean /* @Bean
public ServletServerContainerFactoryBean createWebSocketContainer() { public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(32768); container.setMaxTextMessageBufferSize(32768);
container.setMaxBinaryMessageBufferSize(32768); container.setMaxBinaryMessageBufferSize(32768);
return container; return container;
}*/
@Bean
public HandlerMapping handlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put(WS_PLUGIN_MAPPING, wsHandler());
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map);
mapping.setOrder(-1); // before annotated controllers
return mapping;
} }
@Override @Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
/* @Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*") registry.addHandler(wsHandler(), WS_PLUGIN_MAPPING).setAllowedOrigins("*")
.addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() {
@ -79,19 +98,19 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
//Do nothing //Do nothing
} }
}); });
} }*/
@Bean @Bean
public WebSocketHandler wsHandler() { public WebSocketHandler wsHandler() {
return new TbWebSocketHandler(); return new TbWebSocketHandler();
} }
protected SecurityUser getCurrentUser() throws ThingsboardException { /* protected SecurityUser getCurrentUser() throws ThingsboardException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) {
return (SecurityUser) authentication.getPrincipal(); return (SecurityUser) authentication.getPrincipal();
} else { } else {
throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION);
} }
} }*/
} }

50
application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java

@ -21,10 +21,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.reactive.socket.CloseStatus;
import org.springframework.web.socket.TextMessage; import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.reactive.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -37,10 +36,13 @@ import org.thingsboard.server.service.telemetry.SessionEvent;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.security.Principal;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -48,12 +50,12 @@ import java.util.concurrent.ConcurrentMap;
@Service @Service
@Slf4j @Slf4j
public class TbWebSocketHandler extends TextWebSocketHandler implements TelemetryWebSocketMsgEndpoint { public class TbWebSocketHandler implements WebSocketHandler, TelemetryWebSocketMsgEndpoint {
private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>(); //private static final ConcurrentMap<String, SessionMetaData> internalSessionMap = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>(); // private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>();
@Autowired /* @Autowired
private TelemetryWebSocketService webSocketService; private TelemetryWebSocketService webSocketService;
@Value("${server.ws.limits.max_sessions_per_tenant:0}") @Value("${server.ws.limits.max_sessions_per_tenant:0}")
@ -90,8 +92,26 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} catch (IOException e) { } catch (IOException e) {
log.warn("IO error", e); log.warn("IO error", e);
} }
}*/
@Override
public Mono<Void> handle(WebSocketSession session) {
return session.receive()
.doOnNext(message -> {
Principal principal = session.getHandshakeInfo().getPrincipal().block();
if (principal instanceof SecurityUser) {
SecurityUser currentUser = (SecurityUser) principal;
log.info("[{}][{}] Processing {}", currentUser.getTenantId(), session.getId(), message.getPayloadAsText());
} else {
log.info("[{}] Principal {}", session.getId(), principal);
log.info("[{}] Processing {}", session.getId(), message.getPayloadAsText());
}
})
.then();
} }
/*
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session); super.afterConnectionEstablished(session);
@ -173,11 +193,11 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
this.session = session; this.session = session;
this.sessionRef = sessionRef; this.sessionRef = sessionRef;
} }
} }*/
@Override @Override
public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { public void send(TelemetryWebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException {
String externalId = sessionRef.getSessionId(); /* String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing {}", externalId, msg); log.debug("[{}] Processing {}", externalId, msg);
String internalId = externalSessionMap.get(externalId); String internalId = externalSessionMap.get(externalId);
if (internalId != null) { if (internalId != null) {
@ -212,12 +232,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} else { } else {
log.warn("[{}] Failed to find session by external id", externalId); log.warn("[{}] Failed to find session by external id", externalId);
} }*/
} }
@Override @Override
public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { public void close(TelemetryWebSocketSessionRef sessionRef, CloseStatus reason) throws IOException {
String externalId = sessionRef.getSessionId(); /* String externalId = sessionRef.getSessionId();
log.debug("[{}] Processing close request", externalId); log.debug("[{}] Processing close request", externalId);
String internalId = externalSessionMap.get(externalId); String internalId = externalSessionMap.get(externalId);
if (internalId != null) { if (internalId != null) {
@ -229,10 +249,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} else { } else {
log.warn("[{}] Failed to find session by external id", externalId); log.warn("[{}] Failed to find session by external id", externalId);
} }*/
} }
private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception { /*private boolean checkLimits(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) throws Exception {
String sessionId = session.getId(); String sessionId = session.getId();
if (maxSessionsPerTenant > 0) { if (maxSessionsPerTenant > 0) {
Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); Set<String> tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet());
@ -322,6 +342,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
} }
} }
} }
} }*/
} }

2
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; 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.dao.cassandra.CassandraInstallCluster;
import org.thingsboard.server.service.install.cql.CQLStatementsParser; 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"; private static final String CASSANDRA_DIR = "cassandra";
@Autowired @Autowired
@Qualifier("CassandraInstallCluster")
private CassandraInstallCluster cluster; private CassandraInstallCluster cluster;
@Autowired @Autowired

2
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 com.datastax.driver.core.KeyspaceMetadata;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.cassandra.CassandraCluster;
@ -65,6 +66,7 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
private CassandraCluster cluster; private CassandraCluster cluster;
@Autowired @Autowired
@Qualifier("CassandraInstallCluster")
private CassandraInstallCluster installCluster; private CassandraInstallCluster installCluster;
@Autowired @Autowired

32
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 com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine; 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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
@ -26,7 +28,7 @@ import org.springframework.core.NestedRuntimeException;
import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.ui.velocity.VelocityEngineUtils; //import org.springframework.ui.velocity.VelocityEngineUtils;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
@ -39,6 +41,8 @@ import org.thingsboard.server.dao.settings.AdminSettingsService;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -126,7 +130,7 @@ public class DefaultMailService implements MailService {
Map<String, Object> model = new HashMap<String, Object>(); Map<String, Object> model = new HashMap<String, Object>();
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, String message = mergeTemplateIntoString(this.engine,
"test.vm", UTF_8, model); "test.vm", UTF_8, model);
sendMail(testMailSender, mailFrom, email, subject, message); sendMail(testMailSender, mailFrom, email, subject, message);
@ -141,7 +145,7 @@ public class DefaultMailService implements MailService {
model.put("activationLink", activationLink); model.put("activationLink", activationLink);
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, String message = mergeTemplateIntoString(this.engine,
"activation.vm", UTF_8, model); "activation.vm", UTF_8, model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message);
@ -156,7 +160,7 @@ public class DefaultMailService implements MailService {
model.put("loginLink", loginLink); model.put("loginLink", loginLink);
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, String message = mergeTemplateIntoString(this.engine,
"account.activated.vm", UTF_8, model); "account.activated.vm", UTF_8, model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message);
@ -171,7 +175,7 @@ public class DefaultMailService implements MailService {
model.put("passwordResetLink", passwordResetLink); model.put("passwordResetLink", passwordResetLink);
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, String message = mergeTemplateIntoString(this.engine,
"reset.password.vm", UTF_8, model); "reset.password.vm", UTF_8, model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message);
@ -186,7 +190,7 @@ public class DefaultMailService implements MailService {
model.put("loginLink", loginLink); model.put("loginLink", loginLink);
model.put(TARGET_EMAIL, email); model.put(TARGET_EMAIL, email);
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine, String message = mergeTemplateIntoString(this.engine,
"password.was.reset.vm", UTF_8, model); "password.was.reset.vm", UTF_8, model);
sendMail(mailSender, mailFrom, email, subject, message); sendMail(mailSender, mailFrom, email, subject, message);
@ -225,6 +229,22 @@ public class DefaultMailService implements MailService {
} }
} }
private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation,
String encoding, Map<String, Object> 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<String, Object> model, Writer writer) throws VelocityException {
VelocityContext velocityContext = new VelocityContext(model);
velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer);
}
protected ThingsboardException handleException(Exception exception) { protected ThingsboardException handleException(Exception exception) {
String message; String message;
if (exception instanceof NestedRuntimeException) { if (exception instanceof NestedRuntimeException) {

48
application/src/main/java/org/thingsboard/server/service/security/auth/webflux/WebfluxAuthenticationManager.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2018 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.security.auth.webflux;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import org.thingsboard.server.service.security.auth.JwtAuthenticationToken;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
import reactor.core.publisher.Mono;
@Component
public class WebfluxAuthenticationManager implements ReactiveAuthenticationManager {
@Autowired
private JwtTokenFactory tokenFactory;
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
try {
if (authentication.getCredentials() != null && authentication.getCredentials() instanceof RawAccessJwtToken) {
RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();
SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken);
JwtAuthenticationToken auth = new JwtAuthenticationToken(securityUser);
return Mono.just(auth);
}
return Mono.empty();
} catch (Exception e) {
return Mono.error(e);
}
}
}

86
application/src/main/java/org/thingsboard/server/service/security/auth/webflux/jwt/JwtTokenSecurityContextRepository.java

@ -0,0 +1,86 @@
/**
* Copyright © 2016-2018 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.security.auth.webflux.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.service.security.auth.JwtAuthenticationToken;
import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor;
import org.thingsboard.server.service.security.model.token.RawAccessJwtToken;
import reactor.core.publisher.Mono;
import java.util.List;
@Component
public class JwtTokenSecurityContextRepository implements ServerSecurityContextRepository {
public static final String DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME = "SPRING_SECURITY_CONTEXT";
@Autowired
private ReactiveAuthenticationManager webfluxAuthenticationManager;
@Override
public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
return exchange.getSession()
.doOnNext(session -> {
if (context == null) {
session.getAttributes().remove(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME);
} else {
session.getAttributes().put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context);
}
})
.flatMap(session -> session.changeSessionId());
}
@Override
public Mono<SecurityContext> load(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
String token = extractTokenFromQuery(request);
if (!StringUtils.isEmpty(token)) {
RawAccessJwtToken rawToken = new RawAccessJwtToken(token);
Authentication auth = new JwtAuthenticationToken(rawToken);
return this.webfluxAuthenticationManager.authenticate(auth).map((authentication) -> {
return new SecurityContextImpl(authentication);
});
} else {
return Mono.empty();
}
}
private String extractTokenFromQuery(ServerHttpRequest request) {
String token = null;
if (request.getQueryParams() != null) {
List<String> tokenParamValue = request.getQueryParams().get(ThingsboardSecurityConfiguration.JWT_TOKEN_QUERY_PARAM);
if (tokenParamValue != null && !tokenParamValue.isEmpty()) {
token = tokenParamValue.get(0);
}
}
return token;
}
}

3
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -26,8 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.reactive.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;

2
application/src/main/java/org/thingsboard/server/service/telemetry/TelemetryWebSocketMsgEndpoint.java

@ -15,7 +15,7 @@
*/ */
package org.thingsboard.server.service.telemetry; package org.thingsboard.server.service.telemetry;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.reactive.socket.CloseStatus;
import java.io.IOException; import java.io.IOException;

8
application/src/main/resources/thingsboard.yml

@ -282,11 +282,13 @@ spring.mvc.cors:
# spring serve gzip compressed static resources # spring serve gzip compressed static resources
spring.resources.chain: spring.resources.chain:
gzipped: "true" compressed: "true"
strategy: strategy:
content: content:
enabled: "true" enabled: "true"
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true"
# HSQLDB DAO Configuration # HSQLDB DAO Configuration
spring: spring:
data: data:
@ -320,7 +322,7 @@ spring:
# password: "${SPRING_DATASOURCE_PASSWORD:postgres}" # password: "${SPRING_DATASOURCE_PASSWORD:postgres}"
# Audit log parameters # Audit log parameters
audit_log: audit-log:
# Enable/disable audit log functionality. # Enable/disable audit log functionality.
enabled: "${AUDIT_LOG_ENABLED:true}" enabled: "${AUDIT_LOG_ENABLED:true}"
# Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS # Specify partitioning size for audit log by tenant id storage. Example MINUTES, HOURS, DAYS, MONTHS
@ -329,7 +331,7 @@ audit_log:
default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}" default_query_period: "${AUDIT_LOG_DEFAULT_QUERY_PERIOD:30}"
# Logging levels per each entity type. # Logging levels per each entity type.
# Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations)
logging_level: logging-level:
mask: mask:
"device": "${AUDIT_LOG_MASK_DEVICE:W}" "device": "${AUDIT_LOG_MASK_DEVICE:W}"
"asset": "${AUDIT_LOG_MASK_ASSET:W}" "asset": "${AUDIT_LOG_MASK_ASSET:W}"

4
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(); String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken); 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(); String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), 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(); String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken); 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(); String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),

14
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.common.data.id.UUIDBased;
import org.thingsboard.server.dao.model.ToData; import org.thingsboard.server.dao.model.ToData;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public abstract class DaoUtil { public abstract class DaoUtil {
@ -50,6 +46,14 @@ public abstract class DaoUtil {
return object; return object;
} }
public static <T> T getData(Optional<? extends ToData<T>> data) {
T object = null;
if (data.isPresent()) {
object = data.get().toData();
}
return object;
}
public static UUID getId(UUIDBased idBased) { public static UUID getId(UUIDBased idBased) {
UUID id = null; UUID id = null;
if (idBased != null) { if (idBased != null) {

2
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 @Slf4j
@Service @Service
@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "true")
public class AuditLogServiceImpl implements AuditLogService { public class AuditLogServiceImpl implements AuditLogService {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();

4
dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java

@ -88,11 +88,11 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
protected ExecutorService readResultsProcessingExecutor; protected ExecutorService readResultsProcessingExecutor;
@Value("${audit_log.by_tenant_partitioning}") @Value("${audit-log.by_tenant_partitioning}")
private String partitioning; private String partitioning;
private TsPartitionDate tsFormat; private TsPartitionDate tsFormat;
@Value("${audit_log.default_query_period}") @Value("${audit-log.default_query_period}")
private Integer defaultQueryPeriodInDays; private Integer defaultQueryPeriodInDays;
private PreparedStatement partitionInsertStmt; private PreparedStatement partitionInsertStmt;

2
dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java

@ -33,7 +33,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List; import java.util.List;
@Service @Service
@ConditionalOnProperty(prefix = "audit_log", value = "enabled", havingValue = "false") @ConditionalOnProperty(prefix = "audit-log", value = "enabled", havingValue = "false")
public class DummyAuditLogServiceImpl implements AuditLogService { public class DummyAuditLogServiceImpl implements AuditLogService {
@Override @Override

2
dao/src/main/java/org/thingsboard/server/dao/audit/sink/DummyAuditLogSink.java

@ -20,7 +20,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.audit.AuditLog;
@Component @Component
@ConditionalOnProperty(prefix = "audit_log.sink", value = "type", havingValue = "none") @ConditionalOnProperty(prefix = "audit-log.sink", value = "type", havingValue = "none")
public class DummyAuditLogSink implements AuditLogSink { public class DummyAuditLogSink implements AuditLogSink {
@Override @Override

16
dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java

@ -44,7 +44,7 @@ import java.time.format.DateTimeFormatter;
import java.util.Collections; import java.util.Collections;
@Component @Component
@ConditionalOnProperty(prefix = "audit_log.sink", value = "type", havingValue = "elasticsearch") @ConditionalOnProperty(prefix = "audit-log.sink", value = "type", havingValue = "elasticsearch")
@Slf4j @Slf4j
public class ElasticsearchAuditLogSink implements AuditLogSink { public class ElasticsearchAuditLogSink implements AuditLogSink {
@ -54,19 +54,19 @@ public class ElasticsearchAuditLogSink implements AuditLogSink {
private final ObjectMapper mapper = new ObjectMapper(); private final ObjectMapper mapper = new ObjectMapper();
@Value("${audit_log.sink.index_pattern}") @Value("${audit-log.sink.index_pattern}")
private String indexPattern; private String indexPattern;
@Value("${audit_log.sink.scheme_name}") @Value("${audit-log.sink.scheme_name}")
private String schemeName; private String schemeName;
@Value("${audit_log.sink.host}") @Value("${audit-log.sink.host}")
private String host; private String host;
@Value("${audit_log.sink.port}") @Value("${audit-log.sink.port}")
private int port; private int port;
@Value("${audit_log.sink.user_name}") @Value("${audit-log.sink.user_name}")
private String userName; private String userName;
@Value("${audit_log.sink.password}") @Value("${audit-log.sink.password}")
private String password; private String password;
@Value("${audit_log.sink.date_format}") @Value("${audit-log.sink.date_format}")
private String dateFormat; private String dateFormat;
private RestClient restClient; private RestClient restClient;

43
dao/src/main/java/org/thingsboard/server/dao/cache/TBRedisCacheConfiguration.java

@ -19,14 +19,37 @@ import lombok.Data;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.convert.RedisCustomConversions;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
@Configuration @Configuration
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis", matchIfMissing = false) @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis", matchIfMissing = false)
@ -57,15 +80,12 @@ public class TBRedisCacheConfiguration {
} }
@Bean @Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) { public CacheManager cacheManager(RedisConnectionFactory cf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); DefaultFormattingConversionService redisConversionService = new DefaultFormattingConversionService();
redisTemplate.setConnectionFactory(cf); RedisCacheConfiguration.registerDefaultConverters(redisConversionService);
return redisTemplate; registerDefaultConverters(redisConversionService);
} RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig().withConversionService(redisConversionService);
return RedisCacheManager.builder(cf).cacheDefaults(configuration).build();
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
return new RedisCacheManager(redisTemplate);
} }
@Bean @Bean
@ -73,5 +93,8 @@ public class TBRedisCacheConfiguration {
return new PreviousDeviceCredentialsIdKeyGenerator(); 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);
}
} }

2
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; import javax.annotation.PostConstruct;
@Component @Component("CassandraCluster")
@NoSqlAnyDao @NoSqlAnyDao
public class CassandraCluster extends AbstractCassandraCluster { public class CassandraCluster extends AbstractCassandraCluster {

2
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; import javax.annotation.PostConstruct;
@Component @Component("CassandraInstallCluster")
@NoSqlAnyDao @NoSqlAnyDao
@Profile("install") @Profile("install")
public class CassandraInstallCluster extends AbstractCassandraCluster { public class CassandraInstallCluster extends AbstractCassandraCluster {

2
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 com.datastax.driver.core.exceptions.CodecNotFoundException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; 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.common.data.id.TenantId;
import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.model.type.AuthorityCodec; import org.thingsboard.server.dao.model.type.AuthorityCodec;
@ -44,6 +45,7 @@ import java.util.concurrent.ConcurrentMap;
public abstract class CassandraAbstractDao { public abstract class CassandraAbstractDao {
@Autowired @Autowired
@Qualifier("CassandraCluster")
protected CassandraCluster cluster; protected CassandraCluster cluster;
private ConcurrentMap<String, PreparedStatement> preparedStatementMap = new ConcurrentHashMap<>(); private ConcurrentMap<String, PreparedStatement> preparedStatementMap = new ConcurrentHashMap<>();

9
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 org.thingsboard.server.dao.model.BaseEntity;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
@ -67,23 +68,23 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
@Override @Override
public D findById(TenantId tenantId, UUID key) { public D findById(TenantId tenantId, UUID key) {
log.debug("Get entity by key {}", key); log.debug("Get entity by key {}", key);
E entity = getCrudRepository().findOne(fromTimeUUID(key)); Optional<E> entity = getCrudRepository().findById(fromTimeUUID(key));
return DaoUtil.getData(entity); return DaoUtil.getData(entity);
} }
@Override @Override
public ListenableFuture<D> findByIdAsync(TenantId tenantId, UUID key) { public ListenableFuture<D> findByIdAsync(TenantId tenantId, UUID key) {
log.debug("Get entity by key async {}", 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 @Override
@Transactional @Transactional
public boolean removeById(TenantId tenantId, UUID id) { public boolean removeById(TenantId tenantId, UUID id) {
String key = fromTimeUUID(id); String key = fromTimeUUID(id);
getCrudRepository().delete(key); getCrudRepository().deleteById(key);
log.debug("Remove request: {}", key); log.debug("Remove request: {}", key);
return getCrudRepository().findOne(key) == null; return !getCrudRepository().existsById(key);
} }
@Override @Override

6
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java

@ -52,7 +52,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
AttributeKvCompositeKey compositeKey = AttributeKvCompositeKey compositeKey =
getAttributeKvCompositeKey(entityId, attributeType, attributeKey); getAttributeKvCompositeKey(entityId, attributeType, attributeKey);
return Futures.immediateFuture( return Futures.immediateFuture(
Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findOne(compositeKey)))); Optional.ofNullable(DaoUtil.getData(attributeKvRepository.findById(compositeKey))));
} }
@Override @Override
@ -64,7 +64,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
getAttributeKvCompositeKey(entityId, attributeType, attributeKey)) getAttributeKvCompositeKey(entityId, attributeType, attributeKey))
.collect(Collectors.toList()); .collect(Collectors.toList());
return Futures.immediateFuture( return Futures.immediateFuture(
DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAll(compositeKeys)))); DaoUtil.convertDataList(Lists.newArrayList(attributeKvRepository.findAllById(compositeKeys))));
} }
@Override @Override
@ -103,7 +103,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl
}).collect(Collectors.toList()); }).collect(Collectors.toList());
return service.submit(() -> { return service.submit(() -> {
attributeKvRepository.delete(entitiesToDelete); attributeKvRepository.deleteAll(entitiesToDelete);
return null; return null;
}); });
} }

2
dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java

@ -66,7 +66,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<Comp
if (component.getId() == null) { if (component.getId() == null) {
component.setId(new ComponentDescriptorId(UUIDs.timeBased())); component.setId(new ComponentDescriptorId(UUIDs.timeBased()));
} }
if (componentDescriptorRepository.findOne(UUIDConverter.fromTimeUUID(component.getId().getId())) == null) { if (!componentDescriptorRepository.existsById(UUIDConverter.fromTimeUUID(component.getId().getId()))) {
return Optional.of(save(tenantId, component)); return Optional.of(save(tenantId, component));
} }
return Optional.empty(); return Optional.empty();

8
dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java

@ -97,13 +97,13 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
@Override @Override
public ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<Boolean> checkRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup);
return service.submit(() -> relationRepository.findOne(key) != null); return service.submit(() -> relationRepository.existsById(key));
} }
@Override @Override
public ListenableFuture<EntityRelation> getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { public ListenableFuture<EntityRelation> getRelation(TenantId tenantId, EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) {
RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, 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) { 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) { private boolean deleteRelationIfExists(RelationCompositeKey key) {
boolean relationExistsBeforeDelete = relationRepository.exists(key); boolean relationExistsBeforeDelete = relationRepository.existsById(key);
if (relationExistsBeforeDelete) { if (relationExistsBeforeDelete) {
relationRepository.delete(key); relationRepository.deleteById(key);
} }
return relationExistsBeforeDelete; return relationExistsBeforeDelete;
} }

2
dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java

@ -53,7 +53,7 @@ public interface RelationRepository
RelationEntity save(RelationEntity entity); RelationEntity save(RelationEntity entity);
@Transactional @Transactional
void delete(RelationCompositeKey id); void deleteById(RelationCompositeKey id);
@Transactional @Transactional
void deleteByFromIdAndFromType(String fromId, String fromType); void deleteByFromIdAndFromType(String fromId, String fromType);

6
dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java

@ -258,10 +258,10 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
entityId.getEntityType(), entityId.getEntityType(),
fromTimeUUID(entityId.getId()), fromTimeUUID(entityId.getId()),
key); key);
TsKvLatestEntity entry = tsKvLatestRepository.findOne(compositeKey); Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);
TsKvEntry result; TsKvEntry result;
if (entry != null) { if (entry.isPresent()) {
result = DaoUtil.getData(entry); result = DaoUtil.getData(entry.get());
} else { } else {
result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
} }

8
dao/src/test/resources/application-test.properties

@ -4,10 +4,10 @@ zk.zk_dir=/thingsboard
updates.enabled=false updates.enabled=false
audit_log.enabled=true audit-log.enabled=true
audit_log.by_tenant_partitioning=MONTHS audit-log.by_tenant_partitioning=MONTHS
audit_log.default_query_period=30 audit-log.default_query_period=30
audit_log.sink.type=none audit-log.sink.type=none
cache.type=caffeine cache.type=caffeine
#cache.type=redis #cache.type=redis

1
dao/src/test/resources/sql-test.properties

@ -4,6 +4,7 @@ database.entities.type=sql
sql.ts_inserts_executor_type=fixed sql.ts_inserts_executor_type=fixed
sql.ts_inserts_fixed_thread_pool_size=10 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.show-sql=false
spring.jpa.hibernate.ddl-auto=validate spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect

3
docker/docker-compose.yml

@ -23,6 +23,9 @@ services:
image: "zookeeper:3.5" image: "zookeeper:3.5"
ports: ports:
- "2181" - "2181"
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zookeeper:2888:3888;zookeeper:2181
kafka: kafka:
restart: always restart: always
image: "wurstmeister/kafka" image: "wurstmeister/kafka"

48
pom.xml

@ -29,10 +29,10 @@
<properties> <properties>
<main.dir>${basedir}</main.dir> <main.dir>${basedir}</main.dir>
<spring-boot.version>1.4.3.RELEASE</spring-boot.version> <spring-boot.version>2.1.0.RELEASE</spring-boot.version>
<spring.version>4.3.4.RELEASE</spring.version> <spring.version>5.1.2.RELEASE</spring.version>
<spring-security.version>4.2.0.RELEASE</spring-security.version> <spring-security.version>5.1.1.RELEASE</spring-security.version>
<spring-data-redis.version>1.8.10.RELEASE</spring-data-redis.version> <spring-data-redis.version>2.1.2.RELEASE</spring-data-redis.version>
<jedis.version>2.9.0</jedis.version> <jedis.version>2.9.0</jedis.version>
<jjwt.version>0.7.0</jjwt.version> <jjwt.version>0.7.0</jjwt.version>
<json-path.version>2.2.0</json-path.version> <json-path.version>2.2.0</json-path.version>
@ -41,8 +41,8 @@
<logback.version>1.2.3</logback.version> <logback.version>1.2.3</logback.version>
<mockito.version>1.9.5</mockito.version> <mockito.version>1.9.5</mockito.version>
<rat.version>0.10</rat.version> <rat.version>0.10</rat.version>
<cassandra.version>3.5.0</cassandra.version> <cassandra.version>3.6.0</cassandra.version>
<cassandra-unit.version>3.3.0.2</cassandra-unit.version> <cassandra-unit.version>3.5.0.1</cassandra-unit.version>
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <takari-cpsuite.version>1.2.7</takari-cpsuite.version>
<guava.version>21.0</guava.version> <guava.version>21.0</guava.version>
<caffeine.version>2.6.1</caffeine.version> <caffeine.version>2.6.1</caffeine.version>
@ -50,7 +50,7 @@
<commons-validator.version>1.5.0</commons-validator.version> <commons-validator.version>1.5.0</commons-validator.version>
<commons-io.version>2.5</commons-io.version> <commons-io.version>2.5</commons-io.version>
<commons-csv.version>1.4</commons-csv.version> <commons-csv.version>1.4</commons-csv.version>
<jackson.version>2.8.11.1</jackson.version> <jackson.version>2.9.7</jackson.version>
<json-schema-validator.version>2.2.6</json-schema-validator.version> <json-schema-validator.version>2.2.6</json-schema-validator.version>
<scala.version>2.11</scala.version> <scala.version>2.11</scala.version>
<akka.version>2.4.2</akka.version> <akka.version>2.4.2</akka.version>
@ -58,13 +58,15 @@
<gson.version>2.6.2</gson.version> <gson.version>2.6.2</gson.version>
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<velocity-tools.version>2.0</velocity-tools.version> <velocity-tools.version>2.0</velocity-tools.version>
<antlr.version>2.7.7</antlr.version>
<snakeyaml.version>1.23</snakeyaml.version>
<mail.version>1.4.3</mail.version> <mail.version>1.4.3</mail.version>
<curator.version>4.0.1</curator.version> <curator.version>4.0.1</curator.version>
<protobuf.version>3.0.2</protobuf.version> <protobuf.version>3.6.1</protobuf.version>
<grpc.version>1.12.0</grpc.version> <grpc.version>1.16.1</grpc.version>
<lombok.version>1.16.18</lombok.version> <lombok.version>1.16.18</lombok.version>
<paho.client.version>1.1.0</paho.client.version> <paho.client.version>1.1.0</paho.client.version>
<netty.version>4.1.22.Final</netty.version> <netty.version>4.1.30.Final</netty.version>
<os-maven-plugin.version>1.5.0</os-maven-plugin.version> <os-maven-plugin.version>1.5.0</os-maven-plugin.version>
<rabbitmq.version>4.8.0</rabbitmq.version> <rabbitmq.version>4.8.0</rabbitmq.version>
<surfire.version>2.19.1</surfire.version> <surfire.version>2.19.1</surfire.version>
@ -410,7 +412,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-webflux</artifactId>
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
</dependency> </dependency>
<dependency> <dependency>
@ -485,20 +487,16 @@
<groupId>org.apache.velocity</groupId> <groupId>org.apache.velocity</groupId>
<artifactId>velocity-tools</artifactId> <artifactId>velocity-tools</artifactId>
<version>${velocity-tools.version}</version> <version>${velocity-tools.version}</version>
<exclusions> </dependency>
<exclusion> <dependency>
<groupId>javax.servlet</groupId> <groupId>antlr</groupId>
<artifactId>servlet-api</artifactId> <artifactId>antlr</artifactId>
</exclusion> <version>${antlr.version}</version>
<exclusion> </dependency>
<groupId>dom4j</groupId> <dependency>
<artifactId>dom4j</artifactId> <groupId>org.yaml</groupId>
</exclusion> <artifactId>snakeyaml</artifactId>
<exclusion> <version>${snakeyaml.version}</version>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.rabbitmq</groupId> <groupId>com.rabbitmq</groupId>

Loading…
Cancel
Save