Fix sonar findings (#3015)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
@@ -83,7 +84,7 @@ class ControllerSecurityConfiguration {
|
|||||||
@Order(301)
|
@Order(301)
|
||||||
protected SecurityFilterChain filterChainDDI(
|
protected SecurityFilterChain filterChainDDI(
|
||||||
final HttpSecurity http,
|
final HttpSecurity http,
|
||||||
@Value("${hawkbit.server.security.cors.disable-for-ddi-api:false}") final boolean disableCorsForDdiApi) throws Exception {
|
@Value("${hawkbit.server.security.cors.disable-for-ddi-api:false}") final boolean disableCorsForDdiApi) {
|
||||||
http
|
http
|
||||||
.securityMatcher(DDI_ANT_MATCHERS)
|
.securityMatcher(DDI_ANT_MATCHERS)
|
||||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
||||||
@@ -109,7 +110,7 @@ class ControllerSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (securityProperties.isRequireSsl()) {
|
if (securityProperties.isRequireSsl()) {
|
||||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
http.redirectToHttps(Customizer.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
Mdc.Filter.addMdcFilter(http);
|
Mdc.Filter.addMdcFilter(http);
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2021 Bosch.IO GmbH and others
|
|
||||||
*
|
|
||||||
* This program and the accompanying materials are made
|
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
|
||||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
|
||||||
*/
|
|
||||||
package org.eclipse.hawkbit.amqp;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.springframework.amqp.core.Message;
|
|
||||||
import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that composes a meaningful error message and enhances it with properties from failed message
|
|
||||||
*/
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public final class AmqpErrorMessageComposer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an error message based on failed message content
|
|
||||||
*
|
|
||||||
* @param throwable the throwable containing failed message content
|
|
||||||
* @return meaningful error message
|
|
||||||
*/
|
|
||||||
public static String constructErrorMessage(final Throwable throwable) {
|
|
||||||
final String mainErrorMsg = throwable.getCause().getMessage();
|
|
||||||
if (throwable instanceof ListenerExecutionFailedException listenerExecutionFailedException) {
|
|
||||||
Collection<Message> failedMessages = listenerExecutionFailedException.getFailedMessages();
|
|
||||||
// since the intended message content is always on top of the collection, we only extract the first one
|
|
||||||
final Message failedMessage = failedMessages.iterator().next();
|
|
||||||
final byte[] amqpFailedMsgBody = failedMessage.getBody();
|
|
||||||
final Map<String, Object> amqpFailedMsgHeaders = failedMessage.getMessageProperties().getHeaders();
|
|
||||||
|
|
||||||
final String amqpFailedMsgConcatenatedHeaders = amqpFailedMsgHeaders.keySet().stream()
|
|
||||||
.map(key -> key + "=" + amqpFailedMsgHeaders.get(key))
|
|
||||||
.collect(Collectors.joining(", ", "{", "}"));
|
|
||||||
return mainErrorMsg + new String(amqpFailedMsgBody) + amqpFailedMsgConcatenatedHeaders;
|
|
||||||
}
|
|
||||||
return mainErrorMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -182,6 +182,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
updateAttributesEvent.getTargetAddress());
|
updateAttributesEvent.getTargetAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S4449") // false positive - setCorrelationId param is @Nullable - but in spring - can't annotate it
|
||||||
protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
|
protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
|
||||||
final Message message = MessageBuilder
|
final Message message = MessageBuilder
|
||||||
.withBody(String.valueOf(System.currentTimeMillis()).getBytes())
|
.withBody(String.valueOf(System.currentTimeMillis()).getBytes())
|
||||||
@@ -200,10 +201,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DmfActionRequest actionRequest = new DmfActionRequest(actionId);
|
|
||||||
final Message message = getMessageConverter().toMessage(
|
final Message message = getMessageConverter().toMessage(
|
||||||
actionRequest,
|
new DmfActionRequest(actionId), createConnectorMessagePropertiesEvent(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD));
|
||||||
createConnectorMessagePropertiesEvent(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD));
|
|
||||||
|
|
||||||
amqpSenderService.sendMessage(message, address);
|
amqpSenderService.sendMessage(message, address);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
|||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper;
|
||||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||||
import org.springframework.amqp.support.converter.MessageConverter;
|
import org.springframework.amqp.support.converter.MessageConverter;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
@@ -50,17 +50,18 @@ public class BaseAmqpService {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T convertMessage(@NotNull final Message message, final Class<T> clazz) {
|
public <T> T convertMessage(@NotNull final Message message, final Class<T> clazz) {
|
||||||
checkMessageBody(message);
|
checkMessageBody(message);
|
||||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, clazz.getName());
|
message.getMessageProperties().getHeaders().put(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, clazz.getName());
|
||||||
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S2589") // messageProperties.getContentType() could be null via setContentType
|
||||||
protected static void checkContentTypeJson(final Message message) {
|
protected static void checkContentTypeJson(final Message message) {
|
||||||
final MessageProperties messageProperties = message.getMessageProperties();
|
final MessageProperties messageProperties = message.getMessageProperties();
|
||||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
final String contentType = messageProperties.getContentType();
|
||||||
return;
|
if (contentType == null || !contentType.contains("json")) {
|
||||||
}
|
|
||||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static boolean isMessageBodyEmpty(final Message message) {
|
protected static boolean isMessageBodyEmpty(final Message message) {
|
||||||
return ObjectUtils.isEmpty(message.getBody());
|
return ObjectUtils.isEmpty(message.getBody());
|
||||||
@@ -101,6 +102,6 @@ public class BaseAmqpService {
|
|||||||
* @param message the message to cleaned up
|
* @param message the message to cleaned up
|
||||||
*/
|
*/
|
||||||
protected void cleanMessageHeaderProperties(final Message message) {
|
protected void cleanMessageHeaderProperties(final Message message) {
|
||||||
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
message.getMessageProperties().getHeaders().remove(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ public class DmfApiConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}.
|
* @return {@link RabbitTemplate} with automatic retry, published confirms and {@link JacksonJsonMessageConverter}.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) {
|
public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
|||||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
|
||||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement.Create;
|
import org.eclipse.hawkbit.repository.TargetManagement.Create;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
||||||
@@ -60,9 +59,8 @@ import org.mockito.Mockito;
|
|||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
@@ -96,7 +94,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
|||||||
Create.builder().controllerId(CONTROLLER_ID).securityToken(TEST_TOKEN).address(AMQP_URI.toString()).build());
|
Create.builder().controllerId(CONTROLLER_ID).securityToken(TEST_TOKEN).address(AMQP_URI.toString()).build());
|
||||||
|
|
||||||
this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
|
this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
|
||||||
when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
|
when(rabbitTemplate.getMessageConverter()).thenReturn(new JacksonJsonMessageConverter());
|
||||||
|
|
||||||
senderService = Mockito.mock(DefaultAmqpMessageSenderService.class);
|
senderService = Mockito.mock(DefaultAmqpMessageSenderService.class);
|
||||||
|
|
||||||
@@ -112,7 +110,8 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
|||||||
when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData);
|
when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData);
|
||||||
when(systemManagement.getTenantMetadataWithoutDetails()).thenReturn(tenantMetaData);
|
when(systemManagement.getTenantMetadataWithoutDetails()).thenReturn(tenantMetaData);
|
||||||
|
|
||||||
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
|
amqpMessageDispatcherService = new AmqpMessageDispatcherService(
|
||||||
|
rabbitTemplate, senderService,
|
||||||
artifactUrlHandlerMock, systemManagement, targetManagement,
|
artifactUrlHandlerMock, systemManagement, targetManagement,
|
||||||
softwareModuleManagement, distributionSetManagement, deploymentManagement);
|
softwareModuleManagement, distributionSetManagement, deploymentManagement);
|
||||||
|
|
||||||
@@ -129,8 +128,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testSendDownloadRequestWithSoftwareModulesAndNoArtifacts() {
|
void testSendDownloadRequestWithSoftwareModulesAndNoArtifacts() {
|
||||||
final DistributionSet createDistributionSet = testdataFactory
|
final DistributionSet createDistributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||||
.createDistributionSet(UUID.randomUUID().toString());
|
|
||||||
testdataFactory.addSoftwareModuleMetadata(createDistributionSet);
|
testdataFactory.addSoftwareModuleMetadata(createDistributionSet);
|
||||||
|
|
||||||
final Action action = createAction(createDistributionSet);
|
final Action action = createAction(createDistributionSet);
|
||||||
@@ -377,8 +375,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T convertMessage(final Message message, final Class<T> clazz) {
|
private <T> T convertMessage(final Message message, final Class<T> clazz) {
|
||||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
|
message.getMessageProperties().getHeaders().put(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, clazz.getTypeName());
|
||||||
clazz.getTypeName());
|
|
||||||
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,7 +54,7 @@ class BaseAmqpServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
void convertMessageTest() {
|
void convertMessageTest() {
|
||||||
final DmfActionUpdateStatus actionUpdateStatus = createActionStatus();
|
final DmfActionUpdateStatus actionUpdateStatus = createActionStatus();
|
||||||
when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
|
when(rabbitTemplate.getMessageConverter()).thenReturn(new JacksonJsonMessageConverter());
|
||||||
|
|
||||||
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, createJsonProperties());
|
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, createJsonProperties());
|
||||||
final DmfActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message, DmfActionUpdateStatus.class);
|
final DmfActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message, DmfActionUpdateStatus.class);
|
||||||
@@ -91,7 +91,7 @@ class BaseAmqpServiceTest {
|
|||||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||||
void updateActionStatusWithInvalidJsonContent() {
|
void updateActionStatusWithInvalidJsonContent() {
|
||||||
final Message message = createMessage("Invalid Json".getBytes());
|
final Message message = createMessage("Invalid Json".getBytes());
|
||||||
when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
|
when(rabbitTemplate.getMessageConverter()).thenReturn(new JacksonJsonMessageConverter());
|
||||||
|
|
||||||
assertThatExceptionOfType(MessageConversionException.class)
|
assertThatExceptionOfType(MessageConversionException.class)
|
||||||
.as("Expected MessageConversionException for invalid JSON")
|
.as("Expected MessageConversionException for invalid JSON")
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
|||||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
|
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||||
@@ -88,7 +88,7 @@ public abstract class AbstractAmqpIntegrationTest extends AbstractIntegrationTes
|
|||||||
|
|
||||||
private RabbitTemplate createDmfClient() {
|
private RabbitTemplate createDmfClient() {
|
||||||
final RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
final RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
||||||
template.setMessageConverter(new Jackson2JsonMessageConverter());
|
template.setMessageConverter(new JacksonJsonMessageConverter());
|
||||||
template.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
|
template.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
|
||||||
template.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
|
template.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
|
||||||
template.setExchange(getExchange());
|
template.setExchange(getExchange());
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||||
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;
|
||||||
@@ -32,7 +32,7 @@ public class AmqpTestConfiguration {
|
|||||||
@Primary
|
@Primary
|
||||||
public RabbitTemplate rabbitTemplateForTest(final ConnectionFactory connectionFactory) {
|
public RabbitTemplate rabbitTemplateForTest(final ConnectionFactory connectionFactory) {
|
||||||
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
||||||
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
|
rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter());
|
||||||
rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
|
rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
|
||||||
rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
|
rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
|
||||||
return rabbitTemplate;
|
return rabbitTemplate;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class McpSecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@SuppressWarnings("java:S4502") // CSRF protection is not needed for stateless REST APIs using Authorization header
|
@SuppressWarnings("java:S4502") // CSRF protection is not needed for stateless REST APIs using Authorization header
|
||||||
public SecurityFilterChain mcpSecurityFilterChain(final HttpSecurity http) throws Exception {
|
public SecurityFilterChain mcpSecurityFilterChain(final HttpSecurity http) {
|
||||||
http
|
http
|
||||||
.securityMatcher("/mcp/**")
|
.securityMatcher("/mcp/**")
|
||||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public class MgmtSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (securityProperties.isRequireSsl()) {
|
if (securityProperties.isRequireSsl()) {
|
||||||
http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure());
|
http.redirectToHttps(Customizer.withDefaults());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oauth2ResourceServerCustomizer != null) {
|
if (oauth2ResourceServerCustomizer != null) {
|
||||||
|
|||||||
@@ -63,11 +63,6 @@
|
|||||||
<!-- Database END -->
|
<!-- Database END -->
|
||||||
|
|
||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.springframework.security</groupId>-->
|
|
||||||
<!-- <artifactId>spring-security-test</artifactId>-->
|
|
||||||
<!-- <scope>test</scope>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-security-test</artifactId>
|
<artifactId>spring-boot-starter-security-test</artifactId>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.springframework.data.annotation.CreatedDate;
|
|||||||
import org.springframework.data.annotation.LastModifiedBy;
|
import org.springframework.data.annotation.LastModifiedBy;
|
||||||
import org.springframework.data.annotation.LastModifiedDate;
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,13 +160,13 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final BaseEntity other = (BaseEntity) obj;
|
final BaseEntity other = (BaseEntity) obj;
|
||||||
final Long id = getId();
|
final Long thisId = getId();
|
||||||
final Long otherId = other.getId();
|
final Long otherId = other.getId();
|
||||||
if (id == null) {
|
if (thisId == null) {
|
||||||
if (otherId != null) {
|
if (otherId != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (!id.equals(otherId)) {
|
} else if (!thisId.equals(otherId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return getOptLockRevision() == other.getOptLockRevision();
|
return getOptLockRevision() == other.getOptLockRevision();
|
||||||
@@ -203,9 +204,9 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isController() {
|
protected boolean isController() {
|
||||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
&& SecurityContextHolder.getContext().getAuthentication()
|
return authentication != null
|
||||||
.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails
|
&& authentication.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails
|
||||||
&& tenantAwareDetails.controller();
|
&& tenantAwareDetails.controller();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -35,9 +37,9 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isApprovalNeeded(final Rollout rollout) {
|
public boolean isApprovalNeeded(final Rollout rollout) {
|
||||||
return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class) &&
|
return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class)
|
||||||
hasNoApproveRolloutPermission(
|
&& hasNoApproveRolloutPermission(getCurrentAuthentication().map(Authentication::getAuthorities).orElseGet(List::of).stream()
|
||||||
getCurrentAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());
|
.map(GrantedAuthority::getAuthority).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,11 +49,11 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getApprovalUser(final Rollout rollout) {
|
public String getApprovalUser(final Rollout rollout) {
|
||||||
return getCurrentAuthentication().getName();
|
return getCurrentAuthentication().map(Authentication::getName).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Authentication getCurrentAuthentication() {
|
private static Optional<Authentication> getCurrentAuthentication() {
|
||||||
return SecurityContextHolder.getContext().getAuthentication();
|
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasNoApproveRolloutPermission(final Collection<String> authorities) {
|
private static boolean hasNoApproveRolloutPermission(final Collection<String> authorities) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import jakarta.persistence.PersistenceContext;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
@@ -103,7 +104,7 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
|||||||
private @Nullable BeanFactory beanFactory;
|
private @Nullable BeanFactory beanFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBeanFactory(final BeanFactory beanFactory) {
|
public void setBeanFactory(@NonNull final BeanFactory beanFactory) {
|
||||||
this.beanFactory = beanFactory;
|
this.beanFactory = beanFactory;
|
||||||
super.setBeanFactory(beanFactory);
|
super.setBeanFactory(beanFactory);
|
||||||
}
|
}
|
||||||
@@ -132,7 +133,7 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PersistenceContext
|
@PersistenceContext
|
||||||
public void setEntityManager(final EntityManager entityManager) {
|
public void setEntityManager(@NonNull final EntityManager entityManager) {
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,18 +145,19 @@ public class HawkbitBaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMappingContext(final MappingContext<?, ?> mappingContext) {
|
public void setMappingContext(@NonNull final MappingContext<?, ?> mappingContext) {
|
||||||
super.setMappingContext(mappingContext);
|
super.setMappingContext(mappingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S3776") // this way is more readable
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryFactorySupport doCreateRepositoryFactory() {
|
protected @NonNull RepositoryFactorySupport doCreateRepositoryFactory() {
|
||||||
Objects.requireNonNull(entityManager, "EntityManager must not be null");
|
Objects.requireNonNull(entityManager, "EntityManager must not be null");
|
||||||
final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager) {
|
final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JpaRepositoryImplementation<?, ?> getTargetRepository(
|
protected @NonNull JpaRepositoryImplementation<?, ?> getTargetRepository(
|
||||||
final RepositoryInformation information, final EntityManager entityManager) {
|
@NonNull final RepositoryInformation information, @NonNull final EntityManager entityManager) {
|
||||||
final JpaRepositoryImplementation<?, ?> jpaRepositoryImplementation = super.getTargetRepository(information, entityManager);
|
final JpaRepositoryImplementation<?, ?> jpaRepositoryImplementation = super.getTargetRepository(information, entityManager);
|
||||||
return (JpaRepositoryImplementation<?, ?>) Proxy.newProxyInstance(
|
return (JpaRepositoryImplementation<?, ?>) Proxy.newProxyInstance(
|
||||||
jpaRepositoryImplementation.getClass().getClassLoader(),
|
jpaRepositoryImplementation.getClass().getClassLoader(),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@@ -87,6 +88,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
||||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -111,7 +113,6 @@ import org.springframework.integration.jdbc.lock.JdbcLockRegistry;
|
|||||||
import org.springframework.integration.jdbc.lock.LockRepository;
|
import org.springframework.integration.jdbc.lock.LockRepository;
|
||||||
import org.springframework.integration.support.locks.DefaultLockRegistry;
|
import org.springframework.integration.support.locks.DefaultLockRegistry;
|
||||||
import org.springframework.integration.support.locks.LockRegistry;
|
import org.springframework.integration.support.locks.LockRegistry;
|
||||||
import org.jspecify.annotations.NonNull;
|
|
||||||
import org.springframework.resilience.annotation.EnableResilientMethods;
|
import org.springframework.resilience.annotation.EnableResilientMethods;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||||
@@ -209,8 +210,9 @@ public class JpaRepositoryConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public LockRegistry lockRegistry(final Optional<LockRepository> lockRepository) {
|
@SuppressWarnings("java:S1452") // it could be any LockRegistry<? extends Lock>
|
||||||
return lockRepository.<LockRegistry> map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new);
|
public LockRegistry<? extends Lock> lockRegistry(final Optional<LockRepository> lockRepository) {
|
||||||
|
return lockRepository.<LockRegistry<? extends Lock>> map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -302,7 +304,7 @@ public class JpaRepositoryConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
RolloutHandler rolloutHandler(final RolloutManagement rolloutManagement,
|
RolloutHandler rolloutHandler(final RolloutManagement rolloutManagement,
|
||||||
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
|
final RolloutExecutor rolloutExecutor, final LockRegistry<? extends Lock> lockRegistry,
|
||||||
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
||||||
return new JpaRolloutHandler(rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry);
|
return new JpaRolloutHandler(rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry);
|
||||||
}
|
}
|
||||||
@@ -354,7 +356,7 @@ public class JpaRepositoryConfiguration {
|
|||||||
AutoAssignScheduler autoAssignScheduler(
|
AutoAssignScheduler autoAssignScheduler(
|
||||||
final SystemManagement systemManagement, final AutoAssignHandler autoAssignHandler,
|
final SystemManagement systemManagement, final AutoAssignHandler autoAssignHandler,
|
||||||
@Value("${hawkbit.autoassign.executor.thread-pool.size:1}") final int threadPoolSize,
|
@Value("${hawkbit.autoassign.executor.thread-pool.size:1}") final int threadPoolSize,
|
||||||
final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
|
final Optional<MeterRegistry> meterRegistry) {
|
||||||
return new AutoAssignScheduler(systemManagement, autoAssignHandler, threadPoolSize, meterRegistry);
|
return new AutoAssignScheduler(systemManagement, autoAssignHandler, threadPoolSize, meterRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +379,7 @@ public class JpaRepositoryConfiguration {
|
|||||||
@ConditionalOnProperty(prefix = "hawkbit.autocleanup.scheduler", name = "enabled", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = "hawkbit.autocleanup.scheduler", name = "enabled", matchIfMissing = true)
|
||||||
AutoCleanupScheduler autoCleanupScheduler(
|
AutoCleanupScheduler autoCleanupScheduler(
|
||||||
final List<AutoCleanupScheduler.CleanupTask> cleanupTasks,
|
final List<AutoCleanupScheduler.CleanupTask> cleanupTasks,
|
||||||
final SystemManagement systemManagement, final LockRegistry lockRegistry) {
|
final SystemManagement systemManagement, final LockRegistry<? extends Lock> lockRegistry) {
|
||||||
return new AutoCleanupScheduler(cleanupTasks, systemManagement, lockRegistry);
|
return new AutoCleanupScheduler(cleanupTasks, systemManagement, lockRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ import java.util.Optional;
|
|||||||
import jakarta.persistence.criteria.Root;
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.springframework.data.jpa.domain.DeleteSpecification;
|
import org.springframework.data.jpa.domain.DeleteSpecification;
|
||||||
import org.springframework.data.jpa.domain.PredicateSpecification;
|
import org.springframework.data.jpa.domain.PredicateSpecification;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.data.jpa.domain.UpdateSpecification;
|
import org.springframework.data.jpa.domain.UpdateSpecification;
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface of an extended access control by providing means or fine-grained access control.
|
* Interface of an extended access control by providing means or fine-grained access control.
|
||||||
@@ -47,25 +48,24 @@ public interface AccessController<T> {
|
|||||||
* @param specification is the root specification which needs to be appended by the resource limitation
|
* @param specification is the root specification which needs to be appended by the resource limitation
|
||||||
* @return a new appended specification
|
* @return a new appended specification
|
||||||
*/
|
*/
|
||||||
@Nullable
|
default @NonNull Specification<T> appendAccessRules(final Operation operation, @Nullable final Specification<T> specification) {
|
||||||
default Specification<T> appendAccessRules(final Operation operation, @Nullable final Specification<T> specification) {
|
|
||||||
return getAccessRules(operation)
|
return getAccessRules(operation)
|
||||||
.map(accessRules -> specification == null ? accessRules : specification.and(accessRules))
|
.map(accessRules -> specification == null ? accessRules : specification.and(accessRules))
|
||||||
.orElse(specification);
|
.orElseGet(() -> specification == null ? Specification.unrestricted() : specification);
|
||||||
}
|
}
|
||||||
|
|
||||||
default UpdateSpecification<T> appendAccessRules(final Operation operation, @Nullable final UpdateSpecification<T> specification) {
|
default @NonNull UpdateSpecification<T> appendAccessRules(final Operation operation, @Nullable final UpdateSpecification<T> specification) {
|
||||||
return getAccessRules(operation)
|
return getAccessRules(operation)
|
||||||
.map(this::predicateSpec)
|
.map(this::predicateSpec)
|
||||||
.map(accessRules -> specification == null ? UpdateSpecification.where(accessRules) : specification.and(accessRules))
|
.map(accessRules -> specification == null ? UpdateSpecification.where(accessRules) : specification.and(accessRules))
|
||||||
.orElse(specification);
|
.orElseGet(() -> specification == null ? UpdateSpecification.unrestricted() : specification);
|
||||||
}
|
}
|
||||||
|
|
||||||
default DeleteSpecification<T> appendAccessRules(final Operation operation, @Nullable final DeleteSpecification<T> specification) {
|
default @NonNull DeleteSpecification<T> appendAccessRules(final Operation operation, @Nullable final DeleteSpecification<T> specification) {
|
||||||
return getAccessRules(operation)
|
return getAccessRules(operation)
|
||||||
.map(this::predicateSpec)
|
.map(this::predicateSpec)
|
||||||
.map(accessRules -> specification == null ? DeleteSpecification.where(accessRules) : specification.and(accessRules))
|
.map(accessRules -> specification == null ? DeleteSpecification.where(accessRules) : specification.and(accessRules))
|
||||||
.orElse(specification);
|
.orElseGet(() -> specification == null ? DeleteSpecification.unrestricted() : specification);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +87,8 @@ public interface AccessController<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
// TODO - since Spring 4.x migration, reconsider if it is the best way
|
||||||
|
// shall not be used externally
|
||||||
default PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
default PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
||||||
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class AutoCleanupScheduler {
|
|||||||
private static final String PROP_AUTO_CLEANUP_INTERVAL = "${hawkbit.autocleanup.scheduler.fixedDelay:86400000}";
|
private static final String PROP_AUTO_CLEANUP_INTERVAL = "${hawkbit.autocleanup.scheduler.fixedDelay:86400000}";
|
||||||
|
|
||||||
private final SystemManagement systemManagement;
|
private final SystemManagement systemManagement;
|
||||||
private final LockRegistry lockRegistry;
|
private final LockRegistry<? extends Lock> lockRegistry;
|
||||||
private final List<CleanupTask> cleanupTasks;
|
private final List<CleanupTask> cleanupTasks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,7 +42,7 @@ public class AutoCleanupScheduler {
|
|||||||
*/
|
*/
|
||||||
public AutoCleanupScheduler(
|
public AutoCleanupScheduler(
|
||||||
final List<CleanupTask> cleanupTasks,
|
final List<CleanupTask> cleanupTasks,
|
||||||
final SystemManagement systemManagement, final LockRegistry lockRegistry) {
|
final SystemManagement systemManagement, final LockRegistry<? extends Lock> lockRegistry) {
|
||||||
this.systemManagement = systemManagement;
|
this.systemManagement = systemManagement;
|
||||||
this.lockRegistry = lockRegistry;
|
this.lockRegistry = lockRegistry;
|
||||||
this.cleanupTasks = cleanupTasks;
|
this.cleanupTasks = cleanupTasks;
|
||||||
|
|||||||
@@ -394,8 +394,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
|||||||
log.info("Deleting actions matching rsql {}", rsql);
|
log.info("Deleting actions matching rsql {}", rsql);
|
||||||
actionRepository.delete(DeleteSpecification.where(predicateSpec(QLSupport.getInstance().buildSpec(rsql, ActionFields.class))));
|
actionRepository.delete(DeleteSpecification.where(predicateSpec(QLSupport.getInstance().buildSpec(rsql, ActionFields.class))));
|
||||||
}
|
}
|
||||||
@Deprecated
|
// TODO - since Spring 4.x migration, reconsider if it is the best way
|
||||||
static <T> PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
private static <T> PredicateSpecification<T> predicateSpec(final Specification<T> spec) {
|
||||||
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
return (from, cb) -> spec.toPredicate((Root<T>) from, cb.createQuery(), cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
|||||||
private final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
|
private final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
|
||||||
private final PlatformTransactionManager txManager;
|
private final PlatformTransactionManager txManager;
|
||||||
private final RepositoryProperties repositoryProperties;
|
private final RepositoryProperties repositoryProperties;
|
||||||
private final LockRegistry lockRegistry;
|
private final LockRegistry<? extends Lock> lockRegistry;
|
||||||
|
|
||||||
@SuppressWarnings("java:S107")
|
@SuppressWarnings("java:S107")
|
||||||
protected JpaDistributionSetInvalidationManagement(
|
protected JpaDistributionSetInvalidationManagement(
|
||||||
@@ -56,7 +56,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
|||||||
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
|
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
|
||||||
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
||||||
final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties,
|
final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties,
|
||||||
final LockRegistry lockRegistry) {
|
final LockRegistry<? extends Lock> lockRegistry) {
|
||||||
this.distributionSetManagement = distributionSetManagement;
|
this.distributionSetManagement = distributionSetManagement;
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.deploymentManagement = deploymentManagement;
|
this.deploymentManagement = deploymentManagement;
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ import org.jspecify.annotations.Nullable;
|
|||||||
public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements BaseEntityRepository<T> {
|
public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements BaseEntityRepository<T> {
|
||||||
|
|
||||||
private static final String SPEC_MUST_NOT_BE_NULL = "Specification must not be null";
|
private static final String SPEC_MUST_NOT_BE_NULL = "Specification must not be null";
|
||||||
private static final String APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL =
|
|
||||||
"Appended access rules specification of non-null specification must not be null";
|
|
||||||
|
|
||||||
private final BaseEntityRepository<T> repository;
|
private final BaseEntityRepository<T> repository;
|
||||||
private final AccessController<T> accessController;
|
private final AccessController<T> accessController;
|
||||||
@@ -160,11 +158,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
|||||||
@NonNull
|
@NonNull
|
||||||
public Optional<T> findOne(final Specification<T> spec) {
|
public Optional<T> findOne(final Specification<T> spec) {
|
||||||
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
||||||
return repository.findOne(
|
return repository.findOne(accessController.appendAccessRules(Operation.READ, spec));
|
||||||
// spec shall be non-null and the result of appending rules shall be non-null
|
|
||||||
Objects.requireNonNull(
|
|
||||||
accessController.appendAccessRules(Operation.READ, spec),
|
|
||||||
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -175,51 +169,45 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Page<T> findAll(final Specification<T> spec, @NonNull final Pageable pageable) {
|
public Page<T> findAll(@Nullable final Specification<T> spec, @NonNull final Pageable pageable) {
|
||||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), pageable);
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<T> findAll(final Specification<T> spec, final Specification<T> countSpec, final Pageable pageable) {
|
public Page<T> findAll(@Nullable final Specification<T> spec, @NonNull final Specification<T> countSpec, @NonNull final Pageable pageable) {
|
||||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), countSpec, pageable);
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), countSpec, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public List<T> findAll(final Specification<T> spec, @NonNull final Sort sort) {
|
public List<T> findAll(@Nullable final Specification<T> spec, @NonNull final Sort sort) {
|
||||||
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), sort);
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long count(final Specification<T> spec) {
|
public long count(@Nullable final Specification<T> spec) {
|
||||||
return repository.count(accessController.appendAccessRules(Operation.READ, spec));
|
return repository.count(accessController.appendAccessRules(Operation.READ, spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(@NonNull final Specification<T> spec) {
|
public boolean exists(@NonNull final Specification<T> spec) {
|
||||||
return repository.exists(
|
return repository.exists(accessController.appendAccessRules(Operation.READ, Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL)));
|
||||||
Objects.requireNonNull(accessController.appendAccessRules(Operation.READ, spec)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long update(final UpdateSpecification<T> spec) {
|
public long update(@Nullable final UpdateSpecification<T> spec) {
|
||||||
return repository.update(accessController.appendAccessRules(Operation.UPDATE, spec));
|
return repository.update(accessController.appendAccessRules(Operation.UPDATE, spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long delete(final DeleteSpecification<T> spec) {
|
public long delete(@Nullable final DeleteSpecification<T> spec) {
|
||||||
return repository.delete(accessController.appendAccessRules(Operation.DELETE, spec));
|
return repository.delete(accessController.appendAccessRules(Operation.DELETE, spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S extends T, R> R findBy(final Specification<T> spec, final Function<? super SpecificationFluentQuery<S>, R> queryFunction) {
|
public <S extends T, R> R findBy(@NonNull final Specification<T> spec, final Function<? super SpecificationFluentQuery<S>, R> queryFunction) {
|
||||||
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
||||||
return repository.findBy(
|
return repository.findBy(accessController.appendAccessRules(Operation.READ, spec), queryFunction);
|
||||||
// spec shall be non-null and the result of appending rules shall be non-null
|
|
||||||
Objects.requireNonNull(
|
|
||||||
accessController.appendAccessRules(Operation.READ, spec),
|
|
||||||
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL),
|
|
||||||
queryFunction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -270,11 +258,7 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
|||||||
if (operation == null) {
|
if (operation == null) {
|
||||||
return repository.findOne(spec);
|
return repository.findOne(spec);
|
||||||
} else {
|
} else {
|
||||||
return repository.findOne(
|
return repository.findOne(accessController.appendAccessRules(operation, spec));
|
||||||
// spec shall be non-null and the result of appending rules shall be non-null
|
|
||||||
Objects.requireNonNull(
|
|
||||||
accessController.appendAccessRules(operation, spec),
|
|
||||||
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,20 +273,19 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
|
||||||
public boolean exists(final Operation operation, Specification<T> spec) {
|
public boolean exists(final Operation operation, Specification<T> spec) {
|
||||||
|
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
|
||||||
if (operation == null) {
|
if (operation == null) {
|
||||||
return repository.exists(spec);
|
return repository.exists(spec);
|
||||||
} else {
|
} else {
|
||||||
return repository.exists(
|
return repository.exists(accessController.appendAccessRules(operation, spec));
|
||||||
Objects.requireNonNull(accessController.appendAccessRules(operation, spec)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long count(final Operation operation, @Nullable final Specification<T> spec) {
|
public long count(final Operation operation, @Nullable final Specification<T> spec) {
|
||||||
if (operation == null) {
|
if (operation == null) {
|
||||||
return repository.count(spec);
|
return spec == null ? repository.count() : repository.count(spec);
|
||||||
} else {
|
} else {
|
||||||
return repository.count(accessController.appendAccessRules(operation, spec));
|
return repository.count(accessController.appendAccessRules(operation, spec));
|
||||||
}
|
}
|
||||||
@@ -326,29 +309,25 @@ public class BaseEntityRepositoryACM<T extends AbstractJpaBaseEntity> implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<T> findOne(final Specification<T> spec, final String entityGraph) {
|
public Optional<T> findOne(final Specification<T> spec, final String entityGraph) {
|
||||||
return repository.findOne(
|
return repository.findOne(accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
||||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> findAll(final Specification<T> spec, final String entityGraph) {
|
public List<T> findAll(final Specification<T> spec, final String entityGraph) {
|
||||||
return repository.findAll(
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
||||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<T> findAll(final Specification<T> spec, final String entityGraph, final Pageable pageable) {
|
public Page<T> findAll(final Specification<T> spec, final String entityGraph, final Pageable pageable) {
|
||||||
return repository.findAll(
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph, pageable);
|
||||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph, pageable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> findAll(final Specification<T> spec, final String entityGraph, final Sort sort) {
|
public List<T> findAll(final Specification<T> spec, final String entityGraph, final Sort sort) {
|
||||||
return repository.findAll(
|
return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort);
|
||||||
accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings({"unchecked", "java:S3776"}) // java:S3776 - better readable in one places
|
||||||
static <T extends AbstractJpaBaseEntity, R extends BaseEntityRepository<T>> R of(
|
static <T extends AbstractJpaBaseEntity, R extends BaseEntityRepository<T>> R of(
|
||||||
final R repository, @NonNull final AccessController<T> accessController) {
|
final R repository, @NonNull final AccessController<T> accessController) {
|
||||||
Objects.requireNonNull(repository);
|
Objects.requireNonNull(repository);
|
||||||
|
|||||||
@@ -76,13 +76,14 @@ public class JpaAutoAssignHandler implements AutoAssignHandler {
|
|||||||
private final TargetManagement<? extends Target> targetManagement;
|
private final TargetManagement<? extends Target> targetManagement;
|
||||||
private final DeploymentManagement deploymentManagement;
|
private final DeploymentManagement deploymentManagement;
|
||||||
private final PlatformTransactionManager transactionManager;
|
private final PlatformTransactionManager transactionManager;
|
||||||
private final LockRegistry lockRegistry;
|
private final LockRegistry<? extends Lock> lockRegistry;
|
||||||
private final Optional<MeterRegistry> meterRegistry;
|
private final Optional<MeterRegistry> meterRegistry;
|
||||||
|
|
||||||
public JpaAutoAssignHandler(
|
public JpaAutoAssignHandler(
|
||||||
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
||||||
final TargetManagement<? extends Target> targetManagement, final DeploymentManagement deploymentManagement,
|
final TargetManagement<? extends Target> targetManagement, final DeploymentManagement deploymentManagement,
|
||||||
final PlatformTransactionManager transactionManager, final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
|
final PlatformTransactionManager transactionManager, final LockRegistry<? extends Lock> lockRegistry,
|
||||||
|
final Optional<MeterRegistry> meterRegistry) {
|
||||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||||
this.targetManagement = targetManagement;
|
this.targetManagement = targetManagement;
|
||||||
this.deploymentManagement = deploymentManagement;
|
this.deploymentManagement = deploymentManagement;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
|||||||
|
|
||||||
private final RolloutManagement rolloutManagement;
|
private final RolloutManagement rolloutManagement;
|
||||||
private final RolloutExecutor rolloutExecutor;
|
private final RolloutExecutor rolloutExecutor;
|
||||||
private final LockRegistry lockRegistry;
|
private final LockRegistry<? extends Lock> lockRegistry;
|
||||||
private final PlatformTransactionManager txManager;
|
private final PlatformTransactionManager txManager;
|
||||||
private final Optional<MeterRegistry> meterRegistry;
|
private final Optional<MeterRegistry> meterRegistry;
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
|||||||
* @param txManager transaction manager interface
|
* @param txManager transaction manager interface
|
||||||
*/
|
*/
|
||||||
public JpaRolloutHandler(final RolloutManagement rolloutManagement,
|
public JpaRolloutHandler(final RolloutManagement rolloutManagement,
|
||||||
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
|
final RolloutExecutor rolloutExecutor, final LockRegistry<? extends Lock> lockRegistry,
|
||||||
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.rolloutExecutor = rolloutExecutor;
|
this.rolloutExecutor = rolloutExecutor;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||||
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler.CleanupTask;
|
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler.CleanupTask;
|
||||||
@@ -33,7 +34,7 @@ class AutoCleanupSchedulerTest extends AbstractJpaIntegrationTest {
|
|||||||
private final AtomicInteger counter = new AtomicInteger();
|
private final AtomicInteger counter = new AtomicInteger();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LockRegistry lockRegistry;
|
private LockRegistry<Lock> lockRegistry;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class AutoAssignHandlerTest {
|
|||||||
private PlatformTransactionManager transactionManager;
|
private PlatformTransactionManager transactionManager;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
LockRegistry lockRegistry;
|
LockRegistry<Lock> lockRegistry;
|
||||||
|
|
||||||
private JpaAutoAssignHandler autoAssignHandler;
|
private JpaAutoAssignHandler autoAssignHandler;
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.artifact.ArtifactStorage;
|
import org.eclipse.hawkbit.artifact.ArtifactStorage;
|
||||||
import org.eclipse.hawkbit.artifact.fs.FileArtifactProperties;
|
import org.eclipse.hawkbit.artifact.fs.FileArtifactProperties;
|
||||||
@@ -96,7 +97,7 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
LockRegistry lockRegistry() {
|
LockRegistry<Lock> lockRegistry() {
|
||||||
return new DefaultLockRegistry();
|
return new DefaultLockRegistry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.springframework.amqp.core.Message;
|
|||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.connection.CorrelationData;
|
import org.springframework.amqp.rabbit.connection.CorrelationData;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +88,7 @@ public class DmfSender {
|
|||||||
if (message == null) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
message.getMessageProperties().getHeaders().remove(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
||||||
|
|
||||||
String correlationId = message.getMessageProperties().getCorrelationId();
|
String correlationId = message.getMessageProperties().getCorrelationId();
|
||||||
if (ObjectUtils.isEmpty(correlationId)) {
|
if (ObjectUtils.isEmpty(correlationId)) {
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
|||||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for sender and receiver service.
|
* Abstract class for sender and receiver service.
|
||||||
@@ -57,10 +57,10 @@ public final class VHost extends DmfSender implements MessageListener {
|
|||||||
public VHost(final ConnectionFactory connectionFactory, final AmqpProperties amqpProperties, final boolean initVHost) {
|
public VHost(final ConnectionFactory connectionFactory, final AmqpProperties amqpProperties, final boolean initVHost) {
|
||||||
super(new RabbitTemplate(connectionFactory), amqpProperties);
|
super(new RabbitTemplate(connectionFactory), amqpProperties);
|
||||||
|
|
||||||
// It is necessary to define rabbitTemplate as a Bean and set Jackson2JsonMessageConverter explicitly here in order to convert only
|
// It is necessary to define rabbitTemplate as a Bean and set JacksonJsonMessageConverter explicitly here in order to convert only
|
||||||
// OUTCOMING messages to JSON. In case of INCOMING messages, Jackson2JsonMessageConverter can not handle messages with NULL
|
// OUTCOMING messages to JSON. In case of INCOMING messages, JacksonJsonMessageConverter can not handle messages with NULL
|
||||||
// payload (e.g. REQUEST_ATTRIBUTES_UPDATE), so the SimpleMessageConverter is used instead per default.
|
// payload (e.g. REQUEST_ATTRIBUTES_UPDATE), so the SimpleMessageConverter is used instead per default.
|
||||||
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
|
rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter());
|
||||||
|
|
||||||
if (initVHost) {
|
if (initVHost) {
|
||||||
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
|
final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
|
||||||
@@ -157,21 +157,21 @@ public final class VHost extends DmfSender implements MessageListener {
|
|||||||
*
|
*
|
||||||
* @param message the message to get validated
|
* @param message the message to get validated
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("java:S2589") // messageProperties.getContentType() could be null via setContentType
|
||||||
private static void checkContentTypeJson(final Message message) {
|
private static void checkContentTypeJson(final Message message) {
|
||||||
if (message.getBody().length == 0) {
|
if (message.getBody().length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final MessageProperties messageProperties = message.getMessageProperties();
|
final MessageProperties messageProperties = message.getMessageProperties();
|
||||||
final String headerContentType = (String) messageProperties.getHeaders().get("content-type");
|
final String headerContentType = (String) messageProperties.getHeaders().get("content-type");
|
||||||
if (null != headerContentType) {
|
if (headerContentType != null) {
|
||||||
messageProperties.setContentType(headerContentType);
|
messageProperties.setContentType(headerContentType);
|
||||||
}
|
}
|
||||||
final String contentType = messageProperties.getContentType();
|
final String contentType = messageProperties.getContentType();
|
||||||
if (contentType != null && contentType.contains("json")) {
|
if (contentType == null || !contentType.contains("json")) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleEventMessage(final Message message, final String thingId) {
|
private void handleEventMessage(final Message message, final String thingId) {
|
||||||
final Object eventHeader = message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC);
|
final Object eventHeader = message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC);
|
||||||
@@ -260,11 +260,11 @@ public final class VHost extends DmfSender implements MessageListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a message body to a given class and set the message header AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME for Jackson converter.
|
* Convert a message body to a given class and set the message header DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME for Jackson converter.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T convertMessage(final Message message, final Class<T> clazz) {
|
private <T> T convertMessage(final Message message, final Class<T> clazz) {
|
||||||
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, clazz.getTypeName());
|
message.getMessageProperties().getHeaders().put(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, clazz.getTypeName());
|
||||||
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,12 +20,10 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hawkbit-ui</artifactId>
|
<artifactId>hawkbit-ui</artifactId>
|
||||||
<version>${revision}</version>
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>hawkBit :: UI</name>
|
<name>hawkBit :: UI</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>21</java.version>
|
|
||||||
<vaadin.version>25.0.3</vaadin.version>
|
<vaadin.version>25.0.3</vaadin.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.ui.security;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
|
|
||||||
import com.vaadin.flow.spring.security.VaadinSecurityConfigurer;
|
import com.vaadin.flow.spring.security.VaadinSecurityConfigurer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.hawkbit.ui.view.LoginView;
|
import org.eclipse.hawkbit.ui.view.LoginView;
|
||||||
@@ -21,7 +20,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
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.Import;
|
|
||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
@@ -37,7 +35,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
|||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties(OidcClientProperties.class)
|
@EnableConfigurationProperties(OidcClientProperties.class)
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
|
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
private Customizer<OAuth2LoginConfigurer<HttpSecurity>> oAuth2LoginConfigurerCustomizer;
|
private Customizer<OAuth2LoginConfigurer<HttpSecurity>> oAuth2LoginConfigurerCustomizer;
|
||||||
@@ -64,7 +61,7 @@ public class SecurityConfiguration {
|
|||||||
SecurityFilterChain securityFilterChain(
|
SecurityFilterChain securityFilterChain(
|
||||||
final HttpSecurity http,
|
final HttpSecurity http,
|
||||||
final UserDetailsSetter userDetailsSetter,
|
final UserDetailsSetter userDetailsSetter,
|
||||||
@Autowired(required = false) InMemoryClientRegistrationRepository clientRegistrationRepository) throws Exception {
|
@Autowired(required = false) InMemoryClientRegistrationRepository clientRegistrationRepository) {
|
||||||
http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/images/*.png").permitAll());
|
http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/images/*.png").permitAll());
|
||||||
http.addFilterAfter(userDetailsSetter, AuthorizationFilter.class);
|
http.addFilterAfter(userDetailsSetter, AuthorizationFilter.class);
|
||||||
return http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
|
return http.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ class UserDetailsSetter extends OncePerRequestFilter {
|
|||||||
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
|
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
final Authentication authentication = securityContextHolderStrategy.getContext().getAuthentication();
|
final Authentication authentication = securityContextHolderStrategy.getContext().getAuthentication();
|
||||||
final Authentication newAuthentication;
|
|
||||||
|
|
||||||
if (!(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated()) {
|
final Authentication newAuthentication;
|
||||||
|
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated()) {
|
||||||
final Collection<? extends GrantedAuthority> grantedAuthorities = grantedAuthoritiesService.getGrantedAuthorities(authentication);
|
final Collection<? extends GrantedAuthority> grantedAuthorities = grantedAuthoritiesService.getGrantedAuthorities(authentication);
|
||||||
if (authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken) {
|
if (authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken) {
|
||||||
newAuthentication = new OAuth2AuthenticationToken(
|
newAuthentication = new OAuth2AuthenticationToken(
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.ui.view.util;
|
package org.eclipse.hawkbit.ui.view.util;
|
||||||
|
|
||||||
|
import static java.time.format.FormatStyle.MEDIUM;
|
||||||
|
import static java.time.format.FormatStyle.SHORT;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -31,6 +34,7 @@ import java.util.function.ToLongFunction;
|
|||||||
|
|
||||||
import com.vaadin.flow.component.Component;
|
import com.vaadin.flow.component.Component;
|
||||||
import com.vaadin.flow.component.HasValue;
|
import com.vaadin.flow.component.HasValue;
|
||||||
|
import com.vaadin.flow.component.ModalityMode;
|
||||||
import com.vaadin.flow.component.Text;
|
import com.vaadin.flow.component.Text;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
import com.vaadin.flow.component.Unit;
|
import com.vaadin.flow.component.Unit;
|
||||||
@@ -48,12 +52,14 @@ import com.vaadin.flow.component.notification.Notification;
|
|||||||
import com.vaadin.flow.component.notification.NotificationVariant;
|
import com.vaadin.flow.component.notification.NotificationVariant;
|
||||||
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
|
import com.vaadin.flow.component.page.ExtendedClientDetails;
|
||||||
|
import com.vaadin.flow.component.page.Page.ExtendedClientDetailsReceiver;
|
||||||
import com.vaadin.flow.component.select.Select;
|
import com.vaadin.flow.component.select.Select;
|
||||||
import com.vaadin.flow.component.shared.Tooltip;
|
import com.vaadin.flow.component.shared.Tooltip;
|
||||||
import com.vaadin.flow.component.textfield.NumberField;
|
import com.vaadin.flow.component.textfield.NumberField;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.data.provider.QuerySortOrder;
|
|
||||||
import com.vaadin.flow.data.provider.CallbackDataProvider.FetchCallback;
|
import com.vaadin.flow.data.provider.CallbackDataProvider.FetchCallback;
|
||||||
|
import com.vaadin.flow.data.provider.QuerySortOrder;
|
||||||
import com.vaadin.flow.data.provider.SortDirection;
|
import com.vaadin.flow.data.provider.SortDirection;
|
||||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
||||||
@@ -106,13 +112,13 @@ public class Utils {
|
|||||||
final String label,
|
final String label,
|
||||||
final Consumer<HasValue.ValueChangeEvent<T>> changeListener,
|
final Consumer<HasValue.ValueChangeEvent<T>> changeListener,
|
||||||
final FetchCallback<T, String> listHandler) {
|
final FetchCallback<T, String> listHandler) {
|
||||||
final ComboBox<T> combo = new ComboBox<T>(label, changeListener::accept);
|
final ComboBox<T> combo = new ComboBox<>(label, changeListener::accept);
|
||||||
combo.setAllowedCharPattern(Utils.COMBO_NAME_ALLOWED_CHARS);
|
combo.setAllowedCharPattern(Utils.COMBO_NAME_ALLOWED_CHARS);
|
||||||
combo.setItemsWithFilterConverter(listHandler, nameFilter -> "name==*" + nameFilter + "*");
|
combo.setItemsWithFilterConverter(listHandler, nameFilter -> "name==*" + nameFilter + "*");
|
||||||
return combo;
|
return combo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Button deleteButton(String tooltipText, Runnable deleteAction) {
|
public static Button deleteButton(final String tooltipText, final Runnable deleteAction) {
|
||||||
final Button button = Utils.tooltip(new Button(VaadinIcon.TRASH.create()), tooltipText);
|
final Button button = Utils.tooltip(new Button(VaadinIcon.TRASH.create()), tooltipText);
|
||||||
button.addClickListener(e -> {
|
button.addClickListener(e -> {
|
||||||
ConfirmDialog dialog = Utils.deleteConfirmDialog(deleteAction);
|
ConfirmDialog dialog = Utils.deleteConfirmDialog(deleteAction);
|
||||||
@@ -140,16 +146,12 @@ public class Utils {
|
|||||||
if (addHandler != null) {
|
if (addHandler != null) {
|
||||||
final Button addBtn = tooltip(new Button(VaadinIcon.PLUS.create()), "Add");
|
final Button addBtn = tooltip(new Button(VaadinIcon.PLUS.create()), "Add");
|
||||||
addBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
addBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
addBtn.addClickListener(e -> addHandler
|
addBtn.addClickListener(e -> addHandler.apply(selectionGrid).thenAccept(v -> selectionGrid.refreshGrid(true)));
|
||||||
.apply(selectionGrid)
|
|
||||||
.thenAccept(v -> selectionGrid.refreshGrid(true)));
|
|
||||||
layout.add(addBtn);
|
layout.add(addBtn);
|
||||||
}
|
}
|
||||||
if (removeHandler != null) {
|
if (removeHandler != null) {
|
||||||
final ConfirmDialog dialog = deleteConfirmDialog(
|
final ConfirmDialog dialog = deleteConfirmDialog(
|
||||||
() -> removeHandler
|
() -> removeHandler.apply(selectionGrid).thenAccept(v -> selectionGrid.refreshGrid(false)));
|
||||||
.apply(selectionGrid)
|
|
||||||
.thenAccept(v -> selectionGrid.refreshGrid(false)));
|
|
||||||
final Button removeBtn = tooltip(new Button(VaadinIcon.MINUS.create()), "Remove");
|
final Button removeBtn = tooltip(new Button(VaadinIcon.MINUS.create()), "Remove");
|
||||||
removeBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_CONTRAST);
|
removeBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_CONTRAST);
|
||||||
removeBtn.addClickListener(e -> dialog.open());
|
removeBtn.addClickListener(e -> dialog.open());
|
||||||
@@ -190,11 +192,7 @@ public class Utils {
|
|||||||
public static <T> void remove(final Collection<T> remove, final Set<T> from, final Function<T, ?> idFn) {
|
public static <T> void remove(final Collection<T> remove, final Set<T> from, final Function<T, ?> idFn) {
|
||||||
remove.forEach(toRemove -> {
|
remove.forEach(toRemove -> {
|
||||||
final Object id = idFn.apply(toRemove);
|
final Object id = idFn.apply(toRemove);
|
||||||
for (final Iterator<T> i = from.iterator(); i.hasNext();) {
|
from.removeIf(t -> idFn.apply(t).equals(id));
|
||||||
if (idFn.apply(i.next()).equals(id)) {
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,24 +218,23 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Component> T tooltip(final T component, final String text) {
|
public static <T extends Component> T tooltip(final T component, final String text) {
|
||||||
Tooltip.forComponent(component)
|
Tooltip.forComponent(component).withText(text).withPosition(Tooltip.TooltipPosition.TOP_START);
|
||||||
.withText(text)
|
|
||||||
.withPosition(Tooltip.TooltipPosition.TOP_START);
|
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Icon iconColored(final IconFactory component, final String text, final String color) {
|
public static Icon iconColored(final IconFactory component, final String text, final String color) {
|
||||||
var icon = tooltip(component.create(), text);
|
final Icon icon = tooltip(component.create(), text);
|
||||||
icon.setColor(color);
|
icon.setColor(color);
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Select<MgmtActionType> actionTypeControls(MgmtActionType defaultValue, DateTimePicker forceTime) {
|
public static Select<MgmtActionType> actionTypeControls(final MgmtActionType defaultValue, final DateTimePicker forceTime) {
|
||||||
return actionTypeControls(MgmtActionType.values(), defaultValue, forceTime);
|
return actionTypeControls(MgmtActionType.values(), defaultValue, forceTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Select<MgmtActionType> actionTypeControls(MgmtActionType[] displayedValues, MgmtActionType defaultValue, DateTimePicker forceTime) {
|
public static Select<MgmtActionType> actionTypeControls(
|
||||||
Select<MgmtActionType> actionType = new Select<>();
|
final MgmtActionType[] displayedValues, final MgmtActionType defaultValue, final DateTimePicker forceTime) {
|
||||||
|
final Select<MgmtActionType> actionType = new Select<>();
|
||||||
actionType.setLabel(Constants.ACTION_TYPE);
|
actionType.setLabel(Constants.ACTION_TYPE);
|
||||||
actionType.setItems(displayedValues);
|
actionType.setItems(displayedValues);
|
||||||
actionType.setValue(defaultValue);
|
actionType.setValue(defaultValue);
|
||||||
@@ -269,7 +266,7 @@ public class Utils {
|
|||||||
setHeaderTitle(headerTitle);
|
setHeaderTitle(headerTitle);
|
||||||
setMinWidth(640, Unit.PIXELS);
|
setMinWidth(640, Unit.PIXELS);
|
||||||
|
|
||||||
setModal(true);
|
setModality(ModalityMode.STRICT);
|
||||||
setDraggable(true);
|
setDraggable(true);
|
||||||
setResizable(true);
|
setResizable(true);
|
||||||
|
|
||||||
@@ -298,8 +295,16 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ZoneId getZoneId() {
|
private static ZoneId getZoneId() {
|
||||||
CompletableFuture<ZoneId> zoneId = new CompletableFuture<>();
|
final ExtendedClientDetails details = UI.getCurrent().getPage().getExtendedClientDetails();
|
||||||
UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> zoneId.complete(ZoneId.of(details.getTimeZoneId())));
|
if (details.getScreenWidth() != -1) {
|
||||||
|
// fetched and complete
|
||||||
|
return ZoneId.of(details.getTimeZoneId());
|
||||||
|
} else {
|
||||||
|
// need to refresh
|
||||||
|
final CompletableFuture<ZoneId> zoneId = new CompletableFuture<>();
|
||||||
|
final ExtendedClientDetailsReceiver receiver = refreshedDetails -> zoneId.complete(ZoneId.of(refreshedDetails.getTimeZoneId()));
|
||||||
|
// Placeholder with default values, trigger refresh
|
||||||
|
details.refresh(receiver::receiveDetails);
|
||||||
try {
|
try {
|
||||||
return zoneId.get(1, TimeUnit.SECONDS);
|
return zoneId.get(1, TimeUnit.SECONDS);
|
||||||
} catch (final InterruptedException e) {
|
} catch (final InterruptedException e) {
|
||||||
@@ -309,19 +314,19 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
return ZoneId.systemDefault();
|
return ZoneId.systemDefault();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static <G> LocalDateTimeRenderer<G> localDateTimeRenderer(ToLongFunction<G> f) {
|
public static <G> LocalDateTimeRenderer<G> localDateTimeRenderer(ToLongFunction<G> f) {
|
||||||
|
|
||||||
return new LocalDateTimeRenderer<>((e) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(f.applyAsLong(e)), getZoneId()),
|
return new LocalDateTimeRenderer<>(e -> LocalDateTime.ofInstant(
|
||||||
() -> DateTimeFormatter.ofLocalizedDateTime(
|
Instant.ofEpochMilli(f.applyAsLong(e)), getZoneId()),
|
||||||
FormatStyle.SHORT,
|
() -> DateTimeFormatter.ofLocalizedDateTime(SHORT, MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
||||||
FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String localDateTimeFromTs(long timestamp) {
|
public static String localDateTimeFromTs(long timestamp) {
|
||||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), getZoneId()).format(DateTimeFormatter.ofLocalizedDateTime(
|
return LocalDateTime.ofInstant(
|
||||||
FormatStyle.SHORT,
|
Instant.ofEpochMilli(timestamp),
|
||||||
FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
getZoneId()).format(DateTimeFormatter.ofLocalizedDateTime(SHORT, MEDIUM).withLocale(UI.getCurrent().getLocale()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSortParam(List<QuerySortOrder> querySortOrders) {
|
public static String getSortParam(List<QuerySortOrder> querySortOrders) {
|
||||||
@@ -338,8 +343,8 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String durationFromMillis(Long time) {
|
public static String durationFromMillis(Long time) {
|
||||||
var duration = Duration.between(Instant.ofEpochMilli(time), Instant.now());
|
final Duration duration = Duration.between(Instant.ofEpochMilli(time), Instant.now());
|
||||||
var day = duration.toDaysPart();
|
final long day = duration.toDaysPart();
|
||||||
if (day > 2) {
|
if (day > 2) {
|
||||||
return day + "d";
|
return day + "d";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user