diff --git a/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerSecurityConfiguration.java b/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerSecurityConfiguration.java index 67892b9d0..e61853599 100644 --- a/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerSecurityConfiguration.java +++ b/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerSecurityConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; 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.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -83,7 +84,7 @@ class ControllerSecurityConfiguration { @Order(301) protected SecurityFilterChain filterChainDDI( 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 .securityMatcher(DDI_ANT_MATCHERS) .authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated()) @@ -109,7 +110,7 @@ class ControllerSecurityConfiguration { } if (securityProperties.isRequireSsl()) { - http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure()); + http.redirectToHttps(Customizer.withDefaults()); } Mdc.Filter.addMdcFilter(http); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpErrorMessageComposer.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpErrorMessageComposer.java deleted file mode 100644 index 4d328c8a8..000000000 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpErrorMessageComposer.java +++ /dev/null @@ -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 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 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; - } -} \ No newline at end of file diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index c3b80c24a..aad5e3cd5 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -182,6 +182,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { 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) { final Message message = MessageBuilder .withBody(String.valueOf(System.currentTimeMillis()).getBytes()) @@ -200,10 +201,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { return; } - final DmfActionRequest actionRequest = new DmfActionRequest(actionId); final Message message = getMessageConverter().toMessage( - actionRequest, - createConnectorMessagePropertiesEvent(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD)); + new DmfActionRequest(actionId), createConnectorMessagePropertiesEvent(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD)); amqpSenderService.sendMessage(message, address); } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java index 7daa10d71..d2253bda9 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/BaseAmqpService.java @@ -18,7 +18,7 @@ import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; 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.MessageConverter; import org.springframework.util.ObjectUtils; @@ -50,16 +50,17 @@ public class BaseAmqpService { @SuppressWarnings("unchecked") public T convertMessage(@NotNull final Message message, final Class clazz) { 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); } + @SuppressWarnings("java:S2589") // messageProperties.getContentType() could be null via setContentType protected static void checkContentTypeJson(final Message message) { final MessageProperties messageProperties = message.getMessageProperties(); - if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) { - return; + final String contentType = messageProperties.getContentType(); + 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) { @@ -101,6 +102,6 @@ public class BaseAmqpService { * @param message the message to cleaned up */ protected void cleanMessageHeaderProperties(final Message message) { - message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME); + message.getMessageProperties().getHeaders().remove(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME); } } \ No newline at end of file diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java index ceb999b99..ad1613ef4 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/DmfApiConfiguration.java @@ -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 public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index 161aa684b..1c53eb502 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -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.DmfDownloadAndUpdateRequest; 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.TargetManagement.Create; 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.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper; +import org.springframework.amqp.support.converter.JacksonJsonMessageConverter; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; 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()); this.rabbitTemplate = Mockito.mock(RabbitTemplate.class); - when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter()); + when(rabbitTemplate.getMessageConverter()).thenReturn(new JacksonJsonMessageConverter()); senderService = Mockito.mock(DefaultAmqpMessageSenderService.class); @@ -112,7 +110,8 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData); when(systemManagement.getTenantMetadataWithoutDetails()).thenReturn(tenantMetaData); - amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService, + amqpMessageDispatcherService = new AmqpMessageDispatcherService( + rabbitTemplate, senderService, artifactUrlHandlerMock, systemManagement, targetManagement, softwareModuleManagement, distributionSetManagement, deploymentManagement); @@ -129,8 +128,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { */ @Test void testSendDownloadRequestWithSoftwareModulesAndNoArtifacts() { - final DistributionSet createDistributionSet = testdataFactory - .createDistributionSet(UUID.randomUUID().toString()); + final DistributionSet createDistributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); testdataFactory.addSoftwareModuleMetadata(createDistributionSet); final Action action = createAction(createDistributionSet); @@ -377,8 +375,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { @SuppressWarnings("unchecked") private T convertMessage(final Message message, final Class 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); } } \ No newline at end of file diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java index 9f84a9a7d..5f8a794b4 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/BaseAmqpServiceTest.java @@ -28,7 +28,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; 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; /** @@ -54,7 +54,7 @@ class BaseAmqpServiceTest { @Test void convertMessageTest() { 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 DmfActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message, DmfActionUpdateStatus.class); @@ -91,7 +91,7 @@ class BaseAmqpServiceTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) void updateActionStatusWithInvalidJsonContent() { final Message message = createMessage("Invalid Json".getBytes()); - when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter()); + when(rabbitTemplate.getMessageConverter()).thenReturn(new JacksonJsonMessageConverter()); assertThatExceptionOfType(MessageConversionException.class) .as("Expected MessageConversionException for invalid JSON") diff --git a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AbstractAmqpIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AbstractAmqpIntegrationTest.java index f8e1621d5..a4e2e8e17 100644 --- a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AbstractAmqpIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AbstractAmqpIntegrationTest.java @@ -25,7 +25,7 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; 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.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; @@ -88,7 +88,7 @@ public abstract class AbstractAmqpIntegrationTest extends AbstractIntegrationTes private RabbitTemplate createDmfClient() { final RabbitTemplate template = new RabbitTemplate(connectionFactory); - template.setMessageConverter(new Jackson2JsonMessageConverter()); + template.setMessageConverter(new JacksonJsonMessageConverter()); template.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3)); template.setReplyTimeout(TimeUnit.SECONDS.toMillis(3)); template.setExchange(getExchange()); diff --git a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AmqpTestConfiguration.java b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AmqpTestConfiguration.java index 13e8e49ca..90015ad2a 100644 --- a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AmqpTestConfiguration.java +++ b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/src/main/java/org/eclipse/hawkbit/rabbitmq/test/AmqpTestConfiguration.java @@ -16,7 +16,7 @@ import java.util.concurrent.TimeUnit; import org.springframework.amqp.rabbit.connection.ConnectionFactory; 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.Configuration; import org.springframework.context.annotation.Primary; @@ -32,7 +32,7 @@ public class AmqpTestConfiguration { @Primary public RabbitTemplate rabbitTemplateForTest(final ConnectionFactory connectionFactory) { final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); - rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); + rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter()); rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3)); rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3)); return rabbitTemplate; diff --git a/hawkbit-mcp/hawkbit-mcp-starter/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java b/hawkbit-mcp/hawkbit-mcp-starter/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java index f6a1772ac..a68695b7a 100644 --- a/hawkbit-mcp/hawkbit-mcp-starter/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java +++ b/hawkbit-mcp/hawkbit-mcp-starter/src/main/java/org/eclipse/hawkbit/mcp/server/config/McpSecurityConfiguration.java @@ -61,7 +61,7 @@ public class McpSecurityConfiguration { @Bean @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 .securityMatcher("/mcp/**") .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) diff --git a/hawkbit-mgmt/hawkbit-mgmt-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/MgmtSecurityConfiguration.java b/hawkbit-mgmt/hawkbit-mgmt-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/MgmtSecurityConfiguration.java index 3533e373e..afd951409 100644 --- a/hawkbit-mgmt/hawkbit-mgmt-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/MgmtSecurityConfiguration.java +++ b/hawkbit-mgmt/hawkbit-mgmt-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/MgmtSecurityConfiguration.java @@ -136,7 +136,7 @@ public class MgmtSecurityConfiguration { } if (securityProperties.isRequireSsl()) { - http.requiresChannel(crmRegistry -> crmRegistry.anyRequest().requiresSecure()); + http.redirectToHttps(Customizer.withDefaults()); } if (oauth2ResourceServerCustomizer != null) { diff --git a/hawkbit-monolith/hawkbit-update-server/pom.xml b/hawkbit-monolith/hawkbit-update-server/pom.xml index c08fa7753..0aa164c3d 100644 --- a/hawkbit-monolith/hawkbit-update-server/pom.xml +++ b/hawkbit-monolith/hawkbit-update-server/pom.xml @@ -63,11 +63,6 @@ - - - - - org.springframework.boot spring-boot-starter-security-test diff --git a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java index e70aabdb6..95e15bbf6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java +++ b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/model/AbstractJpaBaseEntity.java @@ -36,6 +36,7 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; /** @@ -159,13 +160,13 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { return false; } final BaseEntity other = (BaseEntity) obj; - final Long id = getId(); + final Long thisId = getId(); final Long otherId = other.getId(); - if (id == null) { + if (thisId == null) { if (otherId != null) { return false; } - } else if (!id.equals(otherId)) { + } else if (!thisId.equals(otherId)) { return false; } return getOptLockRevision() == other.getOptLockRevision(); @@ -203,9 +204,9 @@ public abstract class AbstractJpaBaseEntity implements BaseEntity { } protected boolean isController() { - return SecurityContextHolder.getContext().getAuthentication() != null - && SecurityContextHolder.getContext().getAuthentication() - .getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null + && authentication.getDetails() instanceof TenantAwareAuthenticationDetails tenantAwareDetails && tenantAwareDetails.controller(); } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java index 0e8019998..361d7ca10 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java @@ -10,6 +10,8 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; +import java.util.List; +import java.util.Optional; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -35,9 +37,9 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { */ @Override public boolean isApprovalNeeded(final Rollout rollout) { - return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class) && - hasNoApproveRolloutPermission( - getCurrentAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList()); + return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class) + && hasNoApproveRolloutPermission(getCurrentAuthentication().map(Authentication::getAuthorities).orElseGet(List::of).stream() + .map(GrantedAuthority::getAuthority).toList()); } @Override @@ -47,11 +49,11 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { @Override public String getApprovalUser(final Rollout rollout) { - return getCurrentAuthentication().getName(); + return getCurrentAuthentication().map(Authentication::getName).orElse(null); } - private static Authentication getCurrentAuthentication() { - return SecurityContextHolder.getContext().getAuthentication(); + private static Optional getCurrentAuthentication() { + return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()); } private static boolean hasNoApproveRolloutPermission(final Collection authorities) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitBaseRepositoryFactoryBean.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitBaseRepositoryFactoryBean.java index 43eb8c700..e74fade47 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitBaseRepositoryFactoryBean.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitBaseRepositoryFactoryBean.java @@ -23,6 +23,7 @@ import jakarta.persistence.PersistenceContext; import lombok.extern.slf4j.Slf4j; import org.eclipse.hawkbit.repository.jpa.repository.HawkbitBaseRepository; import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; @@ -103,7 +104,7 @@ public class HawkbitBaseRepositoryFactoryBean, S, ID private @Nullable BeanFactory beanFactory; @Override - public void setBeanFactory(final BeanFactory beanFactory) { + public void setBeanFactory(@NonNull final BeanFactory beanFactory) { this.beanFactory = beanFactory; super.setBeanFactory(beanFactory); } @@ -132,7 +133,7 @@ public class HawkbitBaseRepositoryFactoryBean, S, ID } @PersistenceContext - public void setEntityManager(final EntityManager entityManager) { + public void setEntityManager(@NonNull final EntityManager entityManager) { this.entityManager = entityManager; } @@ -144,18 +145,19 @@ public class HawkbitBaseRepositoryFactoryBean, S, ID } @Override - public void setMappingContext(final MappingContext mappingContext) { + public void setMappingContext(@NonNull final MappingContext mappingContext) { super.setMappingContext(mappingContext); } + @SuppressWarnings("java:S3776") // this way is more readable @Override - protected RepositoryFactorySupport doCreateRepositoryFactory() { + protected @NonNull RepositoryFactorySupport doCreateRepositoryFactory() { Objects.requireNonNull(entityManager, "EntityManager must not be null"); final JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager) { @Override - protected JpaRepositoryImplementation getTargetRepository( - final RepositoryInformation information, final EntityManager entityManager) { + protected @NonNull JpaRepositoryImplementation getTargetRepository( + @NonNull final RepositoryInformation information, @NonNull final EntityManager entityManager) { final JpaRepositoryImplementation jpaRepositoryImplementation = super.getTargetRepository(information, entityManager); return (JpaRepositoryImplementation) Proxy.newProxyInstance( jpaRepositoryImplementation.getClass().getClassLoader(), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java index 40909e120..9c823e4fa 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRepositoryConfiguration.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.List; import java.util.Optional; +import java.util.concurrent.locks.Lock; 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.rsql.VirtualPropertyResolver; import org.eclipse.hawkbit.security.HawkbitSecurityProperties; +import org.jspecify.annotations.NonNull; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; 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.support.locks.DefaultLockRegistry; import org.springframework.integration.support.locks.LockRegistry; -import org.jspecify.annotations.NonNull; import org.springframework.resilience.annotation.EnableResilientMethods; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.security.authorization.AuthorizationDeniedException; @@ -209,8 +210,9 @@ public class JpaRepositoryConfiguration { @Bean @ConditionalOnMissingBean - public LockRegistry lockRegistry(final Optional lockRepository) { - return lockRepository. map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new); + @SuppressWarnings("java:S1452") // it could be any LockRegistry + public LockRegistry lockRegistry(final Optional lockRepository) { + return lockRepository.> map(JdbcLockRegistry::new).orElseGet(DefaultLockRegistry::new); } @Bean @@ -302,7 +304,7 @@ public class JpaRepositoryConfiguration { @Bean @ConditionalOnMissingBean RolloutHandler rolloutHandler(final RolloutManagement rolloutManagement, - final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, + final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final PlatformTransactionManager txManager, final Optional meterRegistry) { return new JpaRolloutHandler(rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry); } @@ -354,7 +356,7 @@ public class JpaRepositoryConfiguration { AutoAssignScheduler autoAssignScheduler( final SystemManagement systemManagement, final AutoAssignHandler autoAssignHandler, @Value("${hawkbit.autoassign.executor.thread-pool.size:1}") final int threadPoolSize, - final LockRegistry lockRegistry, final Optional meterRegistry) { + final Optional meterRegistry) { return new AutoAssignScheduler(systemManagement, autoAssignHandler, threadPoolSize, meterRegistry); } @@ -377,7 +379,7 @@ public class JpaRepositoryConfiguration { @ConditionalOnProperty(prefix = "hawkbit.autocleanup.scheduler", name = "enabled", matchIfMissing = true) AutoCleanupScheduler autoCleanupScheduler( final List cleanupTasks, - final SystemManagement systemManagement, final LockRegistry lockRegistry) { + final SystemManagement systemManagement, final LockRegistry lockRegistry) { return new AutoCleanupScheduler(cleanupTasks, systemManagement, lockRegistry); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java index 32c562c48..f974227bd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessController.java @@ -14,11 +14,12 @@ import java.util.Optional; import jakarta.persistence.criteria.Root; 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.PredicateSpecification; import org.springframework.data.jpa.domain.Specification; 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. @@ -47,25 +48,24 @@ public interface AccessController { * @param specification is the root specification which needs to be appended by the resource limitation * @return a new appended specification */ - @Nullable - default Specification appendAccessRules(final Operation operation, @Nullable final Specification specification) { + default @NonNull Specification appendAccessRules(final Operation operation, @Nullable final Specification specification) { return getAccessRules(operation) .map(accessRules -> specification == null ? accessRules : specification.and(accessRules)) - .orElse(specification); + .orElseGet(() -> specification == null ? Specification.unrestricted() : specification); } - default UpdateSpecification appendAccessRules(final Operation operation, @Nullable final UpdateSpecification specification) { + default @NonNull UpdateSpecification appendAccessRules(final Operation operation, @Nullable final UpdateSpecification specification) { return getAccessRules(operation) .map(this::predicateSpec) .map(accessRules -> specification == null ? UpdateSpecification.where(accessRules) : specification.and(accessRules)) - .orElse(specification); + .orElseGet(() -> specification == null ? UpdateSpecification.unrestricted() : specification); } - default DeleteSpecification appendAccessRules(final Operation operation, @Nullable final DeleteSpecification specification) { + default @NonNull DeleteSpecification appendAccessRules(final Operation operation, @Nullable final DeleteSpecification specification) { return getAccessRules(operation) .map(this::predicateSpec) .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 { } } - @Deprecated + // TODO - since Spring 4.x migration, reconsider if it is the best way + // shall not be used externally default PredicateSpecification predicateSpec(final Specification spec) { return (from, cb) -> spec.toPredicate((Root) from, cb.createQuery(), cb); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java index 9ba6a48b0..e8fbeba14 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupScheduler.java @@ -30,7 +30,7 @@ public class AutoCleanupScheduler { private static final String PROP_AUTO_CLEANUP_INTERVAL = "${hawkbit.autocleanup.scheduler.fixedDelay:86400000}"; private final SystemManagement systemManagement; - private final LockRegistry lockRegistry; + private final LockRegistry lockRegistry; private final List cleanupTasks; /** @@ -42,7 +42,7 @@ public class AutoCleanupScheduler { */ public AutoCleanupScheduler( final List cleanupTasks, - final SystemManagement systemManagement, final LockRegistry lockRegistry) { + final SystemManagement systemManagement, final LockRegistry lockRegistry) { this.systemManagement = systemManagement; this.lockRegistry = lockRegistry; this.cleanupTasks = cleanupTasks; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java index b19c6d117..3c00bd7e3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java @@ -394,8 +394,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl log.info("Deleting actions matching rsql {}", rsql); actionRepository.delete(DeleteSpecification.where(predicateSpec(QLSupport.getInstance().buildSpec(rsql, ActionFields.class)))); } - @Deprecated - static PredicateSpecification predicateSpec(final Specification spec) { + // TODO - since Spring 4.x migration, reconsider if it is the best way + private static PredicateSpecification predicateSpec(final Specification spec) { return (from, cb) -> spec.toPredicate((Root) from, cb.createQuery(), cb); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java index 9f1a6ee3a..cfa860688 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java @@ -48,7 +48,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet private final TargetFilterQueryManagement targetFilterQueryManagement; private final PlatformTransactionManager txManager; private final RepositoryProperties repositoryProperties; - private final LockRegistry lockRegistry; + private final LockRegistry lockRegistry; @SuppressWarnings("java:S107") protected JpaDistributionSetInvalidationManagement( @@ -56,7 +56,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement, final TargetFilterQueryManagement targetFilterQueryManagement, final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties, - final LockRegistry lockRegistry) { + final LockRegistry lockRegistry) { this.distributionSetManagement = distributionSetManagement; this.rolloutManagement = rolloutManagement; this.deploymentManagement = deploymentManagement; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java index abe94c938..11ca58586 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepositoryACM.java @@ -41,8 +41,6 @@ import org.jspecify.annotations.Nullable; public class BaseEntityRepositoryACM implements BaseEntityRepository { 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 repository; private final AccessController accessController; @@ -160,11 +158,7 @@ public class BaseEntityRepositoryACM implements @NonNull public Optional findOne(final Specification spec) { Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL); - return repository.findOne( - // 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)); + return repository.findOne(accessController.appendAccessRules(Operation.READ, spec)); } @Override @@ -175,51 +169,45 @@ public class BaseEntityRepositoryACM implements @Override @NonNull - public Page findAll(final Specification spec, @NonNull final Pageable pageable) { + public Page findAll(@Nullable final Specification spec, @NonNull final Pageable pageable) { return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), pageable); } @Override - public Page findAll(final Specification spec, final Specification countSpec, final Pageable pageable) { + public Page findAll(@Nullable final Specification spec, @NonNull final Specification countSpec, @NonNull final Pageable pageable) { return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), countSpec, pageable); } @Override @NonNull - public List findAll(final Specification spec, @NonNull final Sort sort) { + public List findAll(@Nullable final Specification spec, @NonNull final Sort sort) { return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), sort); } @Override - public long count(final Specification spec) { + public long count(@Nullable final Specification spec) { return repository.count(accessController.appendAccessRules(Operation.READ, spec)); } @Override public boolean exists(@NonNull final Specification spec) { - return repository.exists( - Objects.requireNonNull(accessController.appendAccessRules(Operation.READ, spec))); + return repository.exists(accessController.appendAccessRules(Operation.READ, Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL))); } @Override - public long update(final UpdateSpecification spec) { + public long update(@Nullable final UpdateSpecification spec) { return repository.update(accessController.appendAccessRules(Operation.UPDATE, spec)); } @Override - public long delete(final DeleteSpecification spec) { + public long delete(@Nullable final DeleteSpecification spec) { return repository.delete(accessController.appendAccessRules(Operation.DELETE, spec)); } @Override - public R findBy(final Specification spec, final Function, R> queryFunction) { + public R findBy(@NonNull final Specification spec, final Function, R> queryFunction) { Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL); - return repository.findBy( - // 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); + return repository.findBy(accessController.appendAccessRules(Operation.READ, spec), queryFunction); } @Override @@ -270,11 +258,7 @@ public class BaseEntityRepositoryACM implements if (operation == null) { return repository.findOne(spec); } else { - return repository.findOne( - // 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)); + return repository.findOne(accessController.appendAccessRules(operation, spec)); } } @@ -289,20 +273,19 @@ public class BaseEntityRepositoryACM implements } @Override - @NonNull public boolean exists(final Operation operation, Specification spec) { + Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL); if (operation == null) { return repository.exists(spec); } else { - return repository.exists( - Objects.requireNonNull(accessController.appendAccessRules(operation, spec))); + return repository.exists(accessController.appendAccessRules(operation, spec)); } } @Override public long count(final Operation operation, @Nullable final Specification spec) { if (operation == null) { - return repository.count(spec); + return spec == null ? repository.count() : repository.count(spec); } else { return repository.count(accessController.appendAccessRules(operation, spec)); } @@ -326,29 +309,25 @@ public class BaseEntityRepositoryACM implements @Override public Optional findOne(final Specification spec, final String entityGraph) { - return repository.findOne( - accessController.appendAccessRules(Operation.READ, spec), entityGraph); + return repository.findOne(accessController.appendAccessRules(Operation.READ, spec), entityGraph); } @Override public List findAll(final Specification spec, final String entityGraph) { - return repository.findAll( - accessController.appendAccessRules(Operation.READ, spec), entityGraph); + return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph); } @Override public Page findAll(final Specification spec, final String entityGraph, final Pageable pageable) { - return repository.findAll( - accessController.appendAccessRules(Operation.READ, spec), entityGraph, pageable); + return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph, pageable); } @Override public List findAll(final Specification spec, final String entityGraph, final Sort sort) { - return repository.findAll( - accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort); + return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "java:S3776"}) // java:S3776 - better readable in one places static > R of( final R repository, @NonNull final AccessController accessController) { Objects.requireNonNull(repository); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaAutoAssignHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaAutoAssignHandler.java index 4c65ee500..55106b8ce 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaAutoAssignHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaAutoAssignHandler.java @@ -76,13 +76,14 @@ public class JpaAutoAssignHandler implements AutoAssignHandler { private final TargetManagement targetManagement; private final DeploymentManagement deploymentManagement; private final PlatformTransactionManager transactionManager; - private final LockRegistry lockRegistry; + private final LockRegistry lockRegistry; private final Optional meterRegistry; public JpaAutoAssignHandler( final TargetFilterQueryManagement targetFilterQueryManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, - final PlatformTransactionManager transactionManager, final LockRegistry lockRegistry, final Optional meterRegistry) { + final PlatformTransactionManager transactionManager, final LockRegistry lockRegistry, + final Optional meterRegistry) { this.targetFilterQueryManagement = targetFilterQueryManagement; this.targetManagement = targetManagement; this.deploymentManagement = deploymentManagement; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaRolloutHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaRolloutHandler.java index a1122f9c2..7a7ad107d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaRolloutHandler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/scheduler/JpaRolloutHandler.java @@ -35,7 +35,7 @@ public class JpaRolloutHandler implements RolloutHandler { private final RolloutManagement rolloutManagement; private final RolloutExecutor rolloutExecutor; - private final LockRegistry lockRegistry; + private final LockRegistry lockRegistry; private final PlatformTransactionManager txManager; private final Optional meterRegistry; @@ -48,7 +48,7 @@ public class JpaRolloutHandler implements RolloutHandler { * @param txManager transaction manager interface */ public JpaRolloutHandler(final RolloutManagement rolloutManagement, - final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, + final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final PlatformTransactionManager txManager, final Optional meterRegistry) { this.rolloutManagement = rolloutManagement; this.rolloutExecutor = rolloutExecutor; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupSchedulerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupSchedulerTest.java index 2b9cdd5ae..b2e9dcf0e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupSchedulerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoCleanupSchedulerTest.java @@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; 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.autocleanup.AutoCleanupScheduler.CleanupTask; @@ -33,7 +34,7 @@ class AutoCleanupSchedulerTest extends AbstractJpaIntegrationTest { private final AtomicInteger counter = new AtomicInteger(); @Autowired - private LockRegistry lockRegistry; + private LockRegistry lockRegistry; @BeforeEach void setUp() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/scheduler/AutoAssignHandlerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/scheduler/AutoAssignHandlerTest.java index f563e1499..fc7058e84 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/scheduler/AutoAssignHandlerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/scheduler/AutoAssignHandlerTest.java @@ -58,7 +58,7 @@ class AutoAssignHandlerTest { private PlatformTransactionManager transactionManager; @Mock - LockRegistry lockRegistry; + LockRegistry lockRegistry; private JpaAutoAssignHandler autoAssignHandler; diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java index 1620eb63f..d10295401 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/TestConfiguration.java @@ -15,6 +15,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; import org.eclipse.hawkbit.artifact.ArtifactStorage; import org.eclipse.hawkbit.artifact.fs.FileArtifactProperties; @@ -96,7 +97,7 @@ public class TestConfiguration implements AsyncConfigurer { } @Bean - LockRegistry lockRegistry() { + LockRegistry lockRegistry() { return new DefaultLockRegistry(); } diff --git a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/DmfSender.java b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/DmfSender.java index a81e46945..ed4ac0449 100644 --- a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/DmfSender.java +++ b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/DmfSender.java @@ -30,7 +30,7 @@ import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.connection.CorrelationData; 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; /** @@ -88,7 +88,7 @@ public class DmfSender { if (message == null) { return; } - message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME); + message.getMessageProperties().getHeaders().remove(DefaultJacksonJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME); String correlationId = message.getMessageProperties().getCorrelationId(); if (ObjectUtils.isEmpty(correlationId)) { diff --git a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/VHost.java b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/VHost.java index 861422813..58449d33a 100644 --- a/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/VHost.java +++ b/hawkbit-sdk/hawkbit-sdk-dmf/src/main/java/org/eclipse/hawkbit/sdk/dmf/amqp/VHost.java @@ -39,8 +39,8 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.DefaultJacksonJavaTypeMapper; +import org.springframework.amqp.support.converter.JacksonJsonMessageConverter; /** * 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) { super(new RabbitTemplate(connectionFactory), amqpProperties); - // It is necessary to define rabbitTemplate as a Bean and set Jackson2JsonMessageConverter explicitly here in order to convert only - // OUTCOMING messages to JSON. In case of INCOMING messages, Jackson2JsonMessageConverter can not handle messages with NULL + // 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, JacksonJsonMessageConverter can not handle messages with NULL // payload (e.g. REQUEST_ATTRIBUTES_UPDATE), so the SimpleMessageConverter is used instead per default. - rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); + rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter()); if (initVHost) { final RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); @@ -157,20 +157,20 @@ public final class VHost extends DmfSender implements MessageListener { * * @param message the message to get validated */ + @SuppressWarnings("java:S2589") // messageProperties.getContentType() could be null via setContentType private static void checkContentTypeJson(final Message message) { if (message.getBody().length == 0) { return; } final MessageProperties messageProperties = message.getMessageProperties(); final String headerContentType = (String) messageProperties.getHeaders().get("content-type"); - if (null != headerContentType) { + if (headerContentType != null) { messageProperties.setContentType(headerContentType); } final String contentType = messageProperties.getContentType(); - if (contentType != null && contentType.contains("json")) { - 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"); } private void handleEventMessage(final Message message, final String thingId) { @@ -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") private T convertMessage(final Message message, final Class 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); } } \ No newline at end of file diff --git a/hawkbit-ui/pom.xml b/hawkbit-ui/pom.xml index 5c93de9b2..5fdfa493b 100644 --- a/hawkbit-ui/pom.xml +++ b/hawkbit-ui/pom.xml @@ -20,12 +20,10 @@ hawkbit-ui - ${revision} jar hawkBit :: UI - 21 25.0.3 diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/SecurityConfiguration.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/SecurityConfiguration.java index 762651a60..e6c079c70 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/SecurityConfiguration.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/SecurityConfiguration.java @@ -12,7 +12,6 @@ package org.eclipse.hawkbit.ui.security; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration; import com.vaadin.flow.spring.security.VaadinSecurityConfigurer; import lombok.extern.slf4j.Slf4j; 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -37,7 +35,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand @Configuration @EnableConfigurationProperties(OidcClientProperties.class) @Slf4j -@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class) public class SecurityConfiguration { private Customizer> oAuth2LoginConfigurerCustomizer; @@ -64,7 +61,7 @@ public class SecurityConfiguration { SecurityFilterChain securityFilterChain( final HttpSecurity http, final UserDetailsSetter userDetailsSetter, - @Autowired(required = false) InMemoryClientRegistrationRepository clientRegistrationRepository) throws Exception { + @Autowired(required = false) InMemoryClientRegistrationRepository clientRegistrationRepository) { http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/images/*.png").permitAll()); http.addFilterAfter(userDetailsSetter, AuthorizationFilter.class); return http.with(VaadinSecurityConfigurer.vaadin(), configurer -> { @@ -80,4 +77,4 @@ public class SecurityConfiguration { } }).build(); } -} +} \ No newline at end of file diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/UserDetailsSetter.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/UserDetailsSetter.java index 0340580ef..37bb0621f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/UserDetailsSetter.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/security/UserDetailsSetter.java @@ -44,9 +44,9 @@ class UserDetailsSetter extends OncePerRequestFilter { protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { 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 grantedAuthorities = grantedAuthoritiesService.getGrantedAuthorities(authentication); if (authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken) { newAuthentication = new OAuth2AuthenticationToken( diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java index dcca971a4..c57a4cccb 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java @@ -9,6 +9,9 @@ */ 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.time.Duration; import java.time.Instant; @@ -31,6 +34,7 @@ import java.util.function.ToLongFunction; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasValue; +import com.vaadin.flow.component.ModalityMode; import com.vaadin.flow.component.Text; import com.vaadin.flow.component.UI; 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.orderedlayout.FlexComponent; 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.shared.Tooltip; import com.vaadin.flow.component.textfield.NumberField; 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.QuerySortOrder; import com.vaadin.flow.data.provider.SortDirection; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.data.renderer.LocalDateTimeRenderer; @@ -106,13 +112,13 @@ public class Utils { final String label, final Consumer> changeListener, final FetchCallback listHandler) { - final ComboBox combo = new ComboBox(label, changeListener::accept); + final ComboBox combo = new ComboBox<>(label, changeListener::accept); combo.setAllowedCharPattern(Utils.COMBO_NAME_ALLOWED_CHARS); combo.setItemsWithFilterConverter(listHandler, nameFilter -> "name==*" + nameFilter + "*"); 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); button.addClickListener(e -> { ConfirmDialog dialog = Utils.deleteConfirmDialog(deleteAction); @@ -140,16 +146,12 @@ public class Utils { if (addHandler != null) { final Button addBtn = tooltip(new Button(VaadinIcon.PLUS.create()), "Add"); addBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY); - addBtn.addClickListener(e -> addHandler - .apply(selectionGrid) - .thenAccept(v -> selectionGrid.refreshGrid(true))); + addBtn.addClickListener(e -> addHandler.apply(selectionGrid).thenAccept(v -> selectionGrid.refreshGrid(true))); layout.add(addBtn); } if (removeHandler != null) { final ConfirmDialog dialog = deleteConfirmDialog( - () -> removeHandler - .apply(selectionGrid) - .thenAccept(v -> selectionGrid.refreshGrid(false))); + () -> removeHandler.apply(selectionGrid).thenAccept(v -> selectionGrid.refreshGrid(false))); final Button removeBtn = tooltip(new Button(VaadinIcon.MINUS.create()), "Remove"); removeBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_CONTRAST); removeBtn.addClickListener(e -> dialog.open()); @@ -190,11 +192,7 @@ public class Utils { public static void remove(final Collection remove, final Set from, final Function idFn) { remove.forEach(toRemove -> { final Object id = idFn.apply(toRemove); - for (final Iterator i = from.iterator(); i.hasNext();) { - if (idFn.apply(i.next()).equals(id)) { - i.remove(); - } - } + from.removeIf(t -> idFn.apply(t).equals(id)); }); } @@ -220,24 +218,23 @@ public class Utils { } public static T tooltip(final T component, final String text) { - Tooltip.forComponent(component) - .withText(text) - .withPosition(Tooltip.TooltipPosition.TOP_START); + Tooltip.forComponent(component).withText(text).withPosition(Tooltip.TooltipPosition.TOP_START); return component; } 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); return icon; } - public static Select actionTypeControls(MgmtActionType defaultValue, DateTimePicker forceTime) { + public static Select actionTypeControls(final MgmtActionType defaultValue, final DateTimePicker forceTime) { return actionTypeControls(MgmtActionType.values(), defaultValue, forceTime); } - public static Select actionTypeControls(MgmtActionType[] displayedValues, MgmtActionType defaultValue, DateTimePicker forceTime) { - Select actionType = new Select<>(); + public static Select actionTypeControls( + final MgmtActionType[] displayedValues, final MgmtActionType defaultValue, final DateTimePicker forceTime) { + final Select actionType = new Select<>(); actionType.setLabel(Constants.ACTION_TYPE); actionType.setItems(displayedValues); actionType.setValue(defaultValue); @@ -269,7 +266,7 @@ public class Utils { setHeaderTitle(headerTitle); setMinWidth(640, Unit.PIXELS); - setModal(true); + setModality(ModalityMode.STRICT); setDraggable(true); setResizable(true); @@ -298,30 +295,38 @@ public class Utils { } private static ZoneId getZoneId() { - CompletableFuture zoneId = new CompletableFuture<>(); - UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> zoneId.complete(ZoneId.of(details.getTimeZoneId()))); - try { - return zoneId.get(1, TimeUnit.SECONDS); - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (TimeoutException | ExecutionException ignored) { - log.warn("failed to get zone"); + final ExtendedClientDetails details = UI.getCurrent().getPage().getExtendedClientDetails(); + if (details.getScreenWidth() != -1) { + // fetched and complete + return ZoneId.of(details.getTimeZoneId()); + } else { + // need to refresh + final CompletableFuture zoneId = new CompletableFuture<>(); + final ExtendedClientDetailsReceiver receiver = refreshedDetails -> zoneId.complete(ZoneId.of(refreshedDetails.getTimeZoneId())); + // Placeholder with default values, trigger refresh + details.refresh(receiver::receiveDetails); + try { + return zoneId.get(1, TimeUnit.SECONDS); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (TimeoutException | ExecutionException ignored) { + log.warn("failed to get zone"); + } + return ZoneId.systemDefault(); } - return ZoneId.systemDefault(); } public static LocalDateTimeRenderer localDateTimeRenderer(ToLongFunction f) { - return new LocalDateTimeRenderer<>((e) -> LocalDateTime.ofInstant(Instant.ofEpochMilli(f.applyAsLong(e)), getZoneId()), - () -> DateTimeFormatter.ofLocalizedDateTime( - FormatStyle.SHORT, - FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale())); + return new LocalDateTimeRenderer<>(e -> LocalDateTime.ofInstant( + Instant.ofEpochMilli(f.applyAsLong(e)), getZoneId()), + () -> DateTimeFormatter.ofLocalizedDateTime(SHORT, MEDIUM).withLocale(UI.getCurrent().getLocale())); } public static String localDateTimeFromTs(long timestamp) { - return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), getZoneId()).format(DateTimeFormatter.ofLocalizedDateTime( - FormatStyle.SHORT, - FormatStyle.MEDIUM).withLocale(UI.getCurrent().getLocale())); + return LocalDateTime.ofInstant( + Instant.ofEpochMilli(timestamp), + getZoneId()).format(DateTimeFormatter.ofLocalizedDateTime(SHORT, MEDIUM).withLocale(UI.getCurrent().getLocale())); } public static String getSortParam(List querySortOrders) { @@ -338,8 +343,8 @@ public class Utils { } public static String durationFromMillis(Long time) { - var duration = Duration.between(Instant.ofEpochMilli(time), Instant.now()); - var day = duration.toDaysPart(); + final Duration duration = Duration.between(Instant.ofEpochMilli(time), Instant.now()); + final long day = duration.toDaysPart(); if (day > 2) { return day + "d"; } @@ -348,4 +353,4 @@ public class Utils { .replaceFirst("(^\\d+[HMS]\\d*M*)", "$1") .toLowerCase(); } -} +} \ No newline at end of file