diff --git a/hawkbit-artifact/hawkbit-artifact-api/pom.xml b/hawkbit-artifact/hawkbit-artifact-api/pom.xml
index 7e07f6687b..0ff91f3c09 100644
--- a/hawkbit-artifact/hawkbit-artifact-api/pom.xml
+++ b/hawkbit-artifact/hawkbit-artifact-api/pom.xml
@@ -27,26 +27,5 @@
hawkbit-core${project.version}
-
-
- org.springframework.boot
- spring-boot
-
-
- org.springframework.security
- spring-security-config
-
-
-
- jakarta.validation
- jakarta.validation-api
-
-
-
-
- io.github.classgraph
- classgraph
- test
-
\ No newline at end of file
diff --git a/hawkbit-artifact/hawkbit-artifact-fs/pom.xml b/hawkbit-artifact/hawkbit-artifact-fs/pom.xml
index 76271258b2..f5154305ba 100644
--- a/hawkbit-artifact/hawkbit-artifact-fs/pom.xml
+++ b/hawkbit-artifact/hawkbit-artifact-fs/pom.xml
@@ -27,18 +27,5 @@
hawkbit-artifact-api${project.version}
-
-
- org.springframework
- spring-core
-
-
- org.springframework.boot
- spring-boot-autoconfigure
-
-
- commons-io
- commons-io
-
\ No newline at end of file
diff --git a/hawkbit-artifact/hawkbit-artifact-fs/src/main/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorage.java b/hawkbit-artifact/hawkbit-artifact-fs/src/main/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorage.java
index 24ce23070c..e91259b58d 100644
--- a/hawkbit-artifact/hawkbit-artifact-fs/src/main/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorage.java
+++ b/hawkbit-artifact/hawkbit-artifact-fs/src/main/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorage.java
@@ -19,7 +19,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
-import org.apache.commons.io.FileUtils;
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
import org.eclipse.hawkbit.artifact.ArtifactStorage;
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
@@ -47,7 +46,7 @@ public FileArtifactStorage(final FileArtifactProperties artifactResourceProperti
@Override
public void deleteBySha1(final String tenant, final String sha1) {
- FileUtils.deleteQuietly(getFile(tenant, sha1));
+ deleteSilent(getFile(tenant, sha1));
}
@Override
@@ -65,7 +64,7 @@ public InputStream getBySha1(final String tenant, final String sha1) {
@Override
public void deleteByTenant(final String tenant) {
- FileUtils.deleteQuietly(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
+ deleteSilent(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
}
@Override
@@ -78,7 +77,7 @@ protected void store(final String tenant, final ArtifactHashes base16Hashes, fin
throws IOException {
final File fileSHA1Naming = getFile(tenant, base16Hashes.sha1());
if (fileSHA1Naming.exists()) {
- FileUtils.deleteQuietly(tempFile);
+ deleteSilent(tempFile);
} else {
Files.move(tempFile.toPath(), fileSHA1Naming.toPath());
}
@@ -107,4 +106,24 @@ private Path getSha1DirectoryPath(final String tenant, final String sha1) {
final String folder2 = sha1.substring(length - 2, length);
return Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant), folder1, folder2);
}
+
+ @SuppressWarnings({ "java:S899", "java:S4042" }) // just ignore the result - silent
+ private static void deleteSilent(final File file) {
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ // delete children
+ final File[] children = file.listFiles();
+ if (children != null) {
+ for (final File child : children) {
+ deleteSilent(child);
+ }
+ }
+ }
+ try {
+ file.delete(); // silent
+ } catch (final Exception ignored) {
+ // ignore
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/hawkbit-artifact/hawkbit-artifact-fs/src/test/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorageTest.java b/hawkbit-artifact/hawkbit-artifact-fs/src/test/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorageTest.java
index ec06dbc319..84a1f9098c 100644
--- a/hawkbit-artifact/hawkbit-artifact-fs/src/test/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorageTest.java
+++ b/hawkbit-artifact/hawkbit-artifact-fs/src/test/java/org/eclipse/hawkbit/artifact/fs/FileArtifactStorageTest.java
@@ -15,11 +15,11 @@
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
@@ -54,7 +54,7 @@ static void setup() {
static void afterClass() {
if (new File(artifactResourceProperties.getPath()).exists()) {
try {
- FileUtils.deleteDirectory(new File(artifactResourceProperties.getPath()));
+ delete(new File(artifactResourceProperties.getPath()));
} catch (final IOException | IllegalArgumentException e) {
log.warn("Cannot delete file-directory", e);
}
@@ -69,9 +69,10 @@ void storeSuccessfully() throws IOException {
final byte[] fileContent = randomBytes();
final StoredArtifactInfo artifact = storeRandomArtifact(fileContent);
- final byte[] readContent = new byte[fileContent.length];
- IOUtils.read(artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1()), readContent);
- assertThat(readContent).isEqualTo(fileContent);
+ try (final InputStream is = artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1())) {
+ final byte[] readContent = is.readAllBytes();
+ assertThat(readContent).isEqualTo(fileContent);
+ }
}
/**
@@ -141,4 +142,20 @@ private StoredArtifactInfo storeRandomArtifact(final byte[] fileContent) throws
return artifactFilesystemRepository.store(TENANT, inputStream, "filename.tmp", "application/txt", null);
}
}
+
+ private static void delete(final File file) throws IOException {
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ // delete children
+ final File[] children = file.listFiles();
+ if (children != null) {
+ for (final File child : children) {
+ delete(child);
+ }
+ }
+ }
+
+ Files.delete(file.toPath());
+ }
+ }
}
\ No newline at end of file
diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml
index 6822682cd1..d1c2066ffb 100644
--- a/hawkbit-autoconfigure/pom.xml
+++ b/hawkbit-autoconfigure/pom.xml
@@ -44,13 +44,6 @@
protostuff-coretrue
-
-
- jakarta.servlet
- jakarta.servlet-api
- provided
-
-
io.protostuffprotostuff-runtime
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
index 5910313a21..1c49a58f45 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java
@@ -18,8 +18,8 @@
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.security.autoconfigure.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/StaticUserManagementAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/StaticUserManagementAutoConfiguration.java
index 014b072c37..e35c408e38 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/StaticUserManagementAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/StaticUserManagementAutoConfiguration.java
@@ -11,8 +11,8 @@
import org.eclipse.hawkbit.auth.StaticAuthenticationProvider;
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
-import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.security.autoconfigure.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
diff --git a/hawkbit-core/pom.xml b/hawkbit-core/pom.xml
index 47ee092874..8b88390fd5 100644
--- a/hawkbit-core/pom.xml
+++ b/hawkbit-core/pom.xml
@@ -24,72 +24,43 @@
org.springframework.boot
- spring-boot
-
-
- org.springframework.security
- spring-security-config
+ spring-boot-starter-securityorg.springframework.boot
- spring-boot-actuator-autoconfigure
-
-
- org.springframework
- spring-web
+ spring-boot-starter-security-oauth2-client
- org.springframework.data
- spring-data-commons
-
-
- org.springframework
- spring-context-support
+ org.springframework.boot
+ spring-boot-starter-cache
- org.springframework.security
- spring-security-oauth2-core
+ org.springframework.boot
+ spring-boot-starter-webmvc
+ true
- org.springframework.security
- spring-security-aspects
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ true
- org.springframework.security
- spring-security-web
-
+ org.springframework.boot
+ spring-boot-starter-actuatortrue
- jakarta.validation
- jakarta.validation-api
-
-
- jakarta.servlet
- jakarta.servlet-api
+ tools.jackson.core
+ jackson-databind
- io.micrometer
- micrometer-core
+ jakarta.validation
+ jakarta.validation-apicom.github.ben-manes.caffeinecaffeine
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
- true
-
-
-
-
- io.github.classgraph
- classgraph
- test
-
\ No newline at end of file
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitAutoConfiguration.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitAutoConfiguration.java
new file mode 100644
index 0000000000..1969d71aeb
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/HawkbitAutoConfiguration.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * 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;
+
+import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+
+/**
+ * The {@link HawkbitAutoConfiguration} brings some default configurations needed for hawkbit. It does:
+ *
+ *
Import {@link JacksonAutoConfiguration} and applies hawkbit-jackson-defaults.properties in order to configure the {@link tools.jackson.databind.json.JsonMapper}.
+ *
+ */
+@Configuration
+@Import(JacksonAutoConfiguration.class)
+@PropertySource("classpath:/hawkbit-jackson-defaults.properties")
+public class HawkbitAutoConfiguration {}
\ No newline at end of file
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/auth/StaticAuthenticationProvider.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/auth/StaticAuthenticationProvider.java
index d54accabb5..b400384c17 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/auth/StaticAuthenticationProvider.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/auth/StaticAuthenticationProvider.java
@@ -20,7 +20,7 @@
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAwareUser;
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
-import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.boot.security.autoconfigure.SecurityProperties;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/context/AccessContext.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/context/AccessContext.java
index 4539c4e750..92fc079f16 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/context/AccessContext.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/context/AccessContext.java
@@ -20,8 +20,6 @@
import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -36,6 +34,7 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import tools.jackson.databind.ObjectMapper;
/**
* A 'static' class providing methods related to access context:
@@ -298,11 +297,7 @@ private static String resolve(final Authentication authentication) {
@SuppressWarnings("java:S112") // java:S112 - generic method
private static String serialize(final SecurityContext securityContext) {
Objects.requireNonNull(securityContext);
- try {
- return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
- } catch (final JsonProcessingException e) {
- throw new RuntimeException(e);
- }
+ return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
}
/**
@@ -314,12 +309,7 @@ private static String serialize(final SecurityContext securityContext) {
@SuppressWarnings("java:S112") // java:S112 - generic method
private static SecurityContext deserialize(final String securityContextString) {
Objects.requireNonNull(securityContextString);
- final String securityContextTrimmed = securityContextString.trim();
- try {
- return OBJECT_MAPPER.readerFor(SecCtxInfo.class). readValue(securityContextTrimmed).toSecurityContext();
- } catch (final JsonProcessingException e) {
- throw new RuntimeException(e);
- }
+ return OBJECT_MAPPER.readerFor(SecCtxInfo.class). readValue(securityContextString.trim()).toSecurityContext();
}
private static boolean isAuthenticationInvalid(final Authentication authentication) {
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/DefaultTenantConfiguration.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/DefaultTenantConfiguration.java
index aa0296887c..c7c55b69cc 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/DefaultTenantConfiguration.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/DefaultTenantConfiguration.java
@@ -13,31 +13,29 @@
import java.util.NoSuchElementException;
import java.util.Optional;
+import jakarta.servlet.DispatcherType;
+
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag;
import io.micrometer.observation.ObservationRegistry;
import lombok.NonNull;
import org.eclipse.hawkbit.context.AccessContext;
-import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
-import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
-import org.springframework.boot.actuate.metrics.data.DefaultRepositoryTagsProvider;
-import org.springframework.boot.actuate.metrics.data.RepositoryTagsProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.boot.autoconfigure.security.SecurityProperties;
+import org.springframework.boot.data.metrics.DefaultRepositoryTagsProvider;
+import org.springframework.boot.data.metrics.RepositoryTagsProvider;
+import org.springframework.boot.security.autoconfigure.web.servlet.SecurityFilterProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
-import org.springframework.http.server.observation.ServerRequestObservationConvention;
import org.springframework.web.filter.ServerHttpObservationFilter;
@AutoConfiguration
@@ -52,61 +50,52 @@ TenantAwareCacheManager cacheManager() {
}
@AutoConfiguration(afterName = {
- "org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration",
- "org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration" })
+ "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration",
+ "org.springframework.boot.security.autoconfigure.SecurityAutoConfiguration" })
@ConditionalOnProperty(name = "hawkbit.metrics.tenancy.web.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
- @ConditionalOnClass(name = { "org.springframework.web.servlet.DispatcherServlet", "io.micrometer.observation.Observation" })
+ @ConditionalOnClass(name = { "io.micrometer.observation.Observation", "org.springframework.web.servlet.DispatcherServlet" })
@ConditionalOnBean(ObservationRegistry.class)
public static class WebConfig {
- @Bean
- @Primary
- public DefaultServerRequestObservationConvention serverRequestObservationConvention() {
- return new DefaultServerRequestObservationConvention() {
-
- @NonNull
- @Override
- public KeyValues getLowCardinalityKeyValues(@NonNull final ServerRequestObservationContext context) {
- // Make sure that KeyValues entries are already sorted by name for better performance
- return KeyValues.of(exception(context), method(context), outcome(context), status(context), tenant(), uri(context));
- }
-
- private KeyValue tenant() {
- return KeyValue.of(TENANT_TAG, Optional.ofNullable(AccessContext.tenant()).orElse("n/a"));
- }
- };
- }
-
@Bean
@Primary
public FilterRegistrationBean webMvcObservationFilter(
final ObservationRegistry registry,
- // should be serverRequestObservationConvention (registered above)
- final ObjectProvider customConvention,
- final ObservationProperties observationProperties,
- final SecurityProperties securityProperties) {
- final FilterRegistrationBean filterRegistrationBean = new WebMvcObservationAutoConfiguration()
- .webMvcObservationFilter(registry, customConvention, observationProperties);
+ final SecurityFilterProperties securityFilterProperties) {
+ final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(
+ new ServerHttpObservationFilter(registry, new DefaultServerRequestObservationConvention() {
+
+ @NonNull
+ @Override
+ public KeyValues getLowCardinalityKeyValues(@NonNull final ServerRequestObservationContext context) {
+ // Make sure that KeyValues entries are already sorted by name for better performance
+ return KeyValues.of(exception(context), method(context), outcome(context), status(context), tenant(), uri(context));
+ }
+
+ private static KeyValue tenant() {
+ return KeyValue.of(TENANT_TAG, Optional.ofNullable(AccessContext.tenant()).orElse("n/a"));
+ }
+ }));
+ filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
// after security filter, so to be able to log tenant
- filterRegistrationBean.setOrder(securityProperties.getFilter().getOrder() + 1);
+ filterRegistrationBean.setOrder(securityFilterProperties.getOrder() + 1);
return filterRegistrationBean;
}
}
- @AutoConfiguration(afterName = "org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration")
+ @AutoConfiguration(afterName = "org.springframework.boot.security.autoconfigure.SecurityAutoConfiguration")
+ @ConditionalOnClass(name = "org.springframework.boot.data.metrics.DefaultRepositoryTagsProvider")
@ConditionalOnProperty(name = "hawkbit.metrics.tenancy.repository.enabled", havingValue = "true", matchIfMissing = true)
- @ConditionalOnClass(name = {
- "io.micrometer.core.instrument.Tag",
- "org.springframework.data.repository.core.support.RepositoryMethodInvocationListener" })
public static class RepositoryConfig {
@Bean
public RepositoryTagsProvider repositoryTagsProvider() {
return new DefaultRepositoryTagsProvider() {
+ @NonNull
@Override
- public Iterable repositoryTags(final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) {
+ public Iterable repositoryTags(@NonNull final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) {
final Iterable defaultTags = super.repositoryTags(invocation);
final String tenant = Optional.ofNullable(AccessContext.tenant()).orElse("n/a");
return () -> {
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManager.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManager.java
index 21182cff31..c634aa7e2b 100644
--- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManager.java
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManager.java
@@ -31,7 +31,7 @@
import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* A spring Cache Manager that handles the multi tenancy.
diff --git a/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 1556841081..293a98be98 100644
--- a/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/hawkbit-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,4 @@
+org.eclipse.hawkbit.HawkbitAutoConfiguration
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.WebConfig
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.RepositoryConfig
diff --git a/hawkbit-core/src/main/resources/hawkbit-jackson-defaults.properties b/hawkbit-core/src/main/resources/hawkbit-jackson-defaults.properties
new file mode 100644
index 0000000000..86c26c4fbd
--- /dev/null
+++ b/hawkbit-core/src/main/resources/hawkbit-jackson-defaults.properties
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2015 Bosch Software Innovations 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
+#
+
+# sets MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS to true
+spring.jackson.mapper.allow-final-fields-as-mutators=true
+# sets DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to true
+# this feature allow sending single value instead of array of single value for array fields.
+spring.jackson.deserialization.accept-single-value-as-array=true
\ No newline at end of file
diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/auth/SpPermissionTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/auth/SpPermissionTest.java
index 82ef8940bd..f877209803 100644
--- a/hawkbit-core/src/test/java/org/eclipse/hawkbit/auth/SpPermissionTest.java
+++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/auth/SpPermissionTest.java
@@ -13,7 +13,6 @@
import java.util.Collection;
-import org.eclipse.hawkbit.auth.SpPermission;
import org.junit.jupiter.api.Test;
/**
diff --git a/hawkbit-core/src/test/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManagerTest.java b/hawkbit-core/src/test/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManagerTest.java
index 10e0784bae..85a35aa172 100644
--- a/hawkbit-core/src/test/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManagerTest.java
+++ b/hawkbit-core/src/test/java/org/eclipse/hawkbit/tenancy/TenantAwareCacheManagerTest.java
@@ -11,8 +11,6 @@
import static org.assertj.core.api.Assertions.assertThat;
-import java.util.Collection;
-
import org.junit.jupiter.api.Test;
class TenantAwareCacheManagerTest {
diff --git a/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActivateAutoConfirmation.java b/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActivateAutoConfirmation.java
index f05bd842ff..8fddc37cd3 100644
--- a/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActivateAutoConfirmation.java
+++ b/hawkbit-ddi/hawkbit-ddi-api/src/main/java/org/eclipse/hawkbit/ddi/json/model/DdiActivateAutoConfirmation.java
@@ -14,7 +14,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
diff --git a/hawkbit-ddi/hawkbit-ddi-resource/pom.xml b/hawkbit-ddi/hawkbit-ddi-resource/pom.xml
index a8cbde6851..aa26d4328d 100644
--- a/hawkbit-ddi/hawkbit-ddi-resource/pom.xml
+++ b/hawkbit-ddi/hawkbit-ddi-resource/pom.xml
@@ -40,7 +40,7 @@
- com.fasterxml.jackson.dataformat
+ tools.jackson.dataformatjackson-dataformat-cbor
@@ -64,5 +64,11 @@
${project.version}test
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
\ No newline at end of file
diff --git a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java
index aa046bb561..bf4b6316fb 100644
--- a/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java
+++ b/hawkbit-ddi/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java
@@ -18,20 +18,11 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
-import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
-import com.fasterxml.jackson.dataformat.cbor.CBORParser;
import org.eclipse.hawkbit.context.AccessContext;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
@@ -54,6 +45,11 @@
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import tools.jackson.core.JsonGenerator;
+import tools.jackson.core.JsonParser;
+import tools.jackson.core.json.JsonFactory;
+import tools.jackson.databind.ObjectMapper;
+import tools.jackson.dataformat.cbor.CBORFactory;
@ContextConfiguration(
classes = { DdiApiConfiguration.class, RestConfiguration.class, JpaRepositoryConfiguration.class, TestConfiguration.class })
@@ -86,14 +82,13 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
*
* @param json JSON object to convert
* @return Equivalent CBOR data
- * @throws IOException Invalid JSON input
*/
- protected static byte[] jsonToCbor(final String json) throws IOException {
+ protected static byte[] jsonToCbor(final String json) {
final JsonFactory jsonFactory = new JsonFactory();
final JsonParser jsonParser = jsonFactory.createParser(json);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final CBORFactory cborFactory = new CBORFactory();
- final CBORGenerator cborGenerator = cborFactory.createGenerator(out);
+ final JsonGenerator cborGenerator = cborFactory.createGenerator(out);
while (jsonParser.nextToken() != null) {
cborGenerator.copyCurrentEvent(jsonParser);
}
@@ -106,11 +101,9 @@ protected static byte[] jsonToCbor(final String json) throws IOException {
*
* @param input CBOR data to convert
* @return Equivalent JSON string
- * @throws IOException Invalid CBOR input
*/
- protected static String cborToJson(final byte[] input) throws IOException {
- final CBORFactory cborFactory = new CBORFactory();
- final CBORParser cborParser = cborFactory.createParser(input);
+ protected static String cborToJson(final byte[] input) {
+ final JsonParser cborParser = new CBORFactory().createParser(input);
final JsonFactory jsonFactory = new JsonFactory();
final StringWriter stringWriter = new StringWriter();
final JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
@@ -210,107 +203,92 @@ protected String deploymentBaseLink(final String controllerId, final String acti
controllerId + "/deploymentBase/" + actionId;
}
- protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
+ protected String getJsonRejectedCancelActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
}
- protected String getJsonRejectedDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
+ protected String getJsonRejectedDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
}
- protected String getJsonDownloadDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
+ protected String getJsonDownloadDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
}
- protected String getJsonDownloadedDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
+ protected String getJsonDownloadedDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
}
- protected String getJsonCanceledCancelActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
+ protected String getJsonCanceledCancelActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
}
- protected String getJsonCanceledDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
+ protected String getJsonCanceledDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
}
- protected String getJsonScheduledCancelActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("scheduled"));
+ protected String getJsonScheduledCancelActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS,
+ Collections.singletonList("scheduled"));
}
- protected String getJsonScheduledDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
+ protected String getJsonScheduledDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
}
- protected String getJsonResumedCancelActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
+ protected String getJsonResumedCancelActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
}
- protected String getJsonResumedDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
+ protected String getJsonResumedDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
}
- protected String getJsonProceedingCancelActionFeedback() throws JsonProcessingException {
+ protected String getJsonProceedingCancelActionFeedback() {
return getJsonActionFeedback(
DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.SUCCESS, Collections.singletonList("proceeding"));
}
- protected String getJsonProceedingDeploymentActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
+ protected String getJsonProceedingDeploymentActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
}
- protected String getJsonClosedCancelActionFeedback() throws JsonProcessingException {
- return getJsonActionFeedback(
- DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("closed"));
+ protected String getJsonClosedCancelActionFeedback() {
+ return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("closed"));
}
- protected String getJsonClosedDeploymentActionFeedback() throws JsonProcessingException {
+ protected String getJsonClosedDeploymentActionFeedback() {
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE, Collections.singletonList("closed"));
}
- protected String getJsonActionFeedback(
- final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) throws JsonProcessingException {
+ protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) {
return getJsonActionFeedback(executionStatus, finalResult, Collections.singletonList(randomString(1000)));
}
protected String getJsonActionFeedback(
- final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult,
- final List messages) throws JsonProcessingException {
+ final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult, final List messages) {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages);
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
}
protected String getJsonActionFeedback(
- final DdiStatus.ExecutionStatus executionStatus,
- final DdiResult.FinalResult finalResult, final List messages) throws JsonProcessingException {
+ final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult, final List messages) {
return getJsonActionFeedback(executionStatus, finalResult, null, messages);
}
protected String getJsonActionFeedback(
final DdiStatus.ExecutionStatus executionStatus,
- final DdiResult.FinalResult finalResult, final Integer code, final List messages) throws JsonProcessingException {
+ final DdiResult.FinalResult finalResult, final Integer code, final List messages) {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)), code, messages);
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
}
protected String getJsonConfirmationFeedback(
- final DdiConfirmationFeedback.Confirmation confirmation,
- final Integer code, final List messages) throws JsonProcessingException {
+ final DdiConfirmationFeedback.Confirmation confirmation, final Integer code, final List messages) {
return OBJECT_MAPPER.writeValueAsString(new DdiConfirmationFeedback(confirmation, code, messages));
}
- protected String getJsonInstalledBase(String name, String version) throws JsonProcessingException {
+ protected String getJsonInstalledBase(final String name, final String version) {
return OBJECT_MAPPER.writeValueAsString(new DdiAssignedVersion(name, version));
}
diff --git a/hawkbit-ddi/hawkbit-ddi-server/pom.xml b/hawkbit-ddi/hawkbit-ddi-server/pom.xml
index 85c9852345..39bd7cb6d2 100644
--- a/hawkbit-ddi/hawkbit-ddi-server/pom.xml
+++ b/hawkbit-ddi/hawkbit-ddi-server/pom.xml
@@ -32,6 +32,11 @@
${project.version}
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+
+
com.h2database
diff --git a/hawkbit-ddi/hawkbit-ddi-starter/pom.xml b/hawkbit-ddi/hawkbit-ddi-starter/pom.xml
index 501518a02e..8d13436018 100644
--- a/hawkbit-ddi/hawkbit-ddi-starter/pom.xml
+++ b/hawkbit-ddi/hawkbit-ddi-starter/pom.xml
@@ -53,7 +53,7 @@
org.springframework.boot
- spring-boot-starter-web
+ spring-boot-starter-webmvcorg.springframework.boot
diff --git a/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerDownloadSecurityConfiguration.java b/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerDownloadSecurityConfiguration.java
index 4e8943cab3..2af9de439e 100644
--- a/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerDownloadSecurityConfiguration.java
+++ b/hawkbit-ddi/hawkbit-ddi-starter/src/main/java/org/eclipse/hawkbit/autoconfigure/ddi/ControllerDownloadSecurityConfiguration.java
@@ -76,7 +76,7 @@ public FilterRegistrationBean dosFilterDDIDL(final HawkbitSecurityPro
@Bean
@Order(300) // higher priority than HawkBit DDI security, so that the DDI DL security is applied first
- protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
+ protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) {
http
.securityMatcher(DDI_DL_ANT_MATCHER)
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/pom.xml b/hawkbit-dmf/hawkbit-dmf-amqp/pom.xml
index 958363ba2b..afb2fa9221 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/pom.xml
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/pom.xml
@@ -40,11 +40,7 @@
org.springframework.boot
- spring-boot-autoconfigure
-
-
- org.springframework.amqp
- spring-rabbit
+ spring-boot-starter-amqporg.springframework.security
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 e64f714681..25ab2c5450 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
@@ -218,7 +218,7 @@ protected void targetTriggerUpdateAttributes(final TargetAttributesRequestedServ
protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
final Message message = MessageBuilder
- .withBody(String.valueOf(java.lang.System.currentTimeMillis()).getBytes())
+ .withBody(String.valueOf(System.currentTimeMillis()).getBytes())
.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
.setCorrelationId(ping.getMessageProperties().getCorrelationId())
.setHeader(MessageHeaderKey.TYPE, MessageType.PING_RESPONSE)
@@ -571,7 +571,7 @@ private void sendBatchUpdateMessage(
// due to the fact that all targets in a batch use the same set of software modules we don't generate target-specific urls
final Target firstTarget = targets.get(0);
final DmfBatchDownloadAndUpdateRequest batchRequest = new DmfBatchDownloadAndUpdateRequest(
- java.lang.System.currentTimeMillis(),
+ System.currentTimeMillis(),
dmfTargets,
Optional.ofNullable(modules)
.map(Map::entrySet)
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 434ea0faf9..0b3521ca13 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
@@ -43,18 +43,19 @@
import org.springframework.amqp.rabbit.listener.FatalExceptionStrategy;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
-import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
+import org.springframework.boot.amqp.autoconfigure.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
-import org.springframework.retry.backoff.ExponentialBackOffPolicy;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.ErrorHandler;
+import tools.jackson.databind.json.JsonMapper;
/**
* Spring configuration for AMQP based DMF communication for indirect device integration.
@@ -112,16 +113,19 @@ public RabbitAdmin rabbitAdmin() {
}
/**
- * @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() {
+ public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
- rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
+ rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter(jsonMapper));
- final RetryTemplate retryTemplate = new RetryTemplate();
- retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
- rabbitTemplate.setRetryTemplate(retryTemplate);
+ // the same policy the previously used default ExponentialBackOffPolicy applied
+ rabbitTemplate.setRetryTemplate(new RetryTemplate(RetryPolicy.builder()
+ .delay(Duration.ofMillis(100))
+ .multiplier(2)
+ .maxDelay(Duration.ofSeconds(30))
+ .build()));
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
@@ -238,8 +242,8 @@ public AmqpMessageHandlerService amqpMessageHandlerService(
*/
@Bean
@ConditionalOnMissingBean
- public AmqpMessageSenderService amqpSenderServiceBean() {
- return new DefaultAmqpMessageSenderService(rabbitTemplate());
+ public AmqpMessageSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
+ return new DefaultAmqpMessageSenderService(rabbitTemplate);
}
/**
diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
index 4a9168e5f3..2f2171e733 100644
--- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
+++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java
@@ -56,7 +56,7 @@
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;
import org.springframework.amqp.support.converter.MessageConverter;
@@ -110,7 +110,7 @@ class AmqpMessageHandlerServiceTest {
@SuppressWarnings({ "rawtypes", "unchecked" })
void before() {
TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagement);
- messageConverter = new Jackson2JsonMessageConverter();
+ messageConverter = new JacksonJsonMessageConverter();
lenient().when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
final TenantConfigurationValue multiAssignmentConfig = TenantConfigurationValue.builder().value(Boolean.FALSE)
.global(Boolean.FALSE).build();
diff --git a/hawkbit-dmf/hawkbit-dmf-api/lombok.config b/hawkbit-dmf/hawkbit-dmf-api/lombok.config
new file mode 100644
index 0000000000..4429ae4ee6
--- /dev/null
+++ b/hawkbit-dmf/hawkbit-dmf-api/lombok.config
@@ -0,0 +1,2 @@
+# Disable nullability annotations in order to do not bring jpecify as a dependency
+lombok.addNullAnnotations=none
\ No newline at end of file
diff --git a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/pom.xml b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/pom.xml
index fa61f26af5..1ce4295c3a 100644
--- a/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/pom.xml
+++ b/hawkbit-dmf/hawkbit-dmf-rabbitmq-test/pom.xml
@@ -52,23 +52,15 @@
com.rabbitmqhttp-client
- compileorg.springframework.boot
- spring-boot-starter-web
- compile
-
-
- org.springframework.amqp
- spring-rabbit-test
- compile
+ spring-boot-starter-webmvcorg.eclipse.hawkbithawkbit-repository-test${project.version}
- compileorg.awaitility
diff --git a/hawkbit-dmf/hawkbit-dmf-server/pom.xml b/hawkbit-dmf/hawkbit-dmf-server/pom.xml
index 1a7c08d53b..1608374cb4 100644
--- a/hawkbit-dmf/hawkbit-dmf-server/pom.xml
+++ b/hawkbit-dmf/hawkbit-dmf-server/pom.xml
@@ -45,11 +45,6 @@
org.mariadb.jdbcmariadb-java-client
-
-
- jakarta.servlet
- jakarta.servlet-api
-
diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/pom.xml b/hawkbit-mgmt/hawkbit-mgmt-api/pom.xml
index 91e2901432..cbdf6e3fb5 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-api/pom.xml
+++ b/hawkbit-mgmt/hawkbit-mgmt-api/pom.xml
@@ -31,5 +31,11 @@
hawkbit-rest-api${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-hateoas-test
+ test
+
\ No newline at end of file
diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
index 64a89c81b1..46e788733b 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/json/model/target/MgmtDistributionSetAssignment.java
@@ -8,6 +8,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
@@ -17,6 +18,7 @@
/**
* Request Body of DistributionSet for assignment operations (ID only).
*/
+@NoArgsConstructor
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java
index a9a1b2036f..2ce64654b9 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtRestConstants.java
@@ -98,7 +98,7 @@ public final class MgmtRestConstants {
public static final int REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE = Integer
.parseInt(REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT);
/**
- * The base URL mapping for the spring acuator management context path.
+ * The base URL mapping for the spring actuator management context path.
*/
public static final String BASE_SYSTEM_MAPPING = "/system";
public static final String SYSTEM_V1_REQUEST_MAPPING = BASE_V1_REQUEST_MAPPING + BASE_SYSTEM_MAPPING;
diff --git a/hawkbit-mgmt/hawkbit-mgmt-api/src/test/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentResponseBodyTest.java b/hawkbit-mgmt/hawkbit-mgmt-api/src/test/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentResponseBodyTest.java
index 0c887bb42d..a335662dce 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-api/src/test/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentResponseBodyTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-api/src/test/java/org/eclipse/hawkbit/mgmt/json/model/distributionset/MgmtTargetAssignmentResponseBodyTest.java
@@ -41,13 +41,11 @@ void testActionIdsSerialization() throws IOException {
assertThat(jsonNode.has("assigned")).as("the assigned targets count").isTrue();
assertThat(jsonNode.get("assigned").isNumber()).as("the assigned targets count").isTrue();
- assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count")
- .isEqualTo(ASSIGNED_ACTIONS.size());
+ assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count").isEqualTo(ASSIGNED_ACTIONS.size());
assertThat(jsonNode.has("alreadyAssigned")).as("the already assigned targets count").isTrue();
assertThat(jsonNode.get("alreadyAssigned").isNumber()).as("the already assigned targets count").isTrue();
- assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count")
- .isEqualTo(ALREADY_ASSIGNED_COUNT);
+ assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count").isEqualTo(ALREADY_ASSIGNED_COUNT);
assertThat(jsonNode.has("total")).as("the total targets count").isTrue();
assertThat(jsonNode.get("total").isNumber()).as("the total targets count").isTrue();
diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/pom.xml b/hawkbit-mgmt/hawkbit-mgmt-resource/pom.xml
index 90a10b6228..cc84145bbe 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-resource/pom.xml
+++ b/hawkbit-mgmt/hawkbit-mgmt-resource/pom.xml
@@ -59,5 +59,11 @@
${project.version}test
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
\ No newline at end of file
diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtApiConfiguration.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtApiConfiguration.java
index 991e813c0f..07eb2f5d0b 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtApiConfiguration.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtApiConfiguration.java
@@ -17,6 +17,7 @@
import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.stereotype.Controller;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Enable {@link ComponentScan} in the resource package to set up all {@link Controller} annotated classes and set up the REST-Resources
@@ -27,4 +28,4 @@
@ComponentScan
@Import({ RestConfiguration.class, OpenApi.class })
@PropertySource("classpath:/hawkbit-mgmt-api-defaults.properties")
-public class MgmtApiConfiguration {}
\ No newline at end of file
+public class MgmtApiConfiguration implements WebMvcConfigurer {}
\ No newline at end of file
diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtBasicAuthResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtBasicAuthResourceTest.java
index 684345e4c7..b415143fb3 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtBasicAuthResourceTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtBasicAuthResourceTest.java
@@ -32,8 +32,8 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext;
diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
index 81c31e75b5..2eb1bc7583 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java
@@ -89,7 +89,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
+import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
@@ -142,8 +142,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
ActionRepository actionRepository;
@Autowired
private JpaProperties jpaProperties;
- @Autowired
- private ObjectMapper objectMapper;
/**
* Ensures that when targetType value of -1 is provided the target type is unassigned from the target.
@@ -251,7 +249,7 @@ void postActivateAutoConfirm() throws Exception {
final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value", "custom_remark_value");
mvc.perform(post(TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + TARGET_V1_AUTO_CONFIRM + "/" + TARGET_V1_ACTIVATE_AUTO_CONFIRM,
testTarget.getControllerId())
- .content(objectMapper.writeValueAsString(body)).contentType(APPLICATION_JSON))
+ .content(OBJECT_MAPPER.writeValueAsString(body)).contentType(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNoContent());
}
diff --git a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTypeResourceTest.java b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTypeResourceTest.java
index 5439120630..2282676652 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTypeResourceTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetTypeResourceTest.java
@@ -564,13 +564,13 @@ void invalidRequestsOnTargetTypesResource() throws Exception {
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
- mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("{\"id\":1}")
- .contentType(MediaType.APPLICATION_JSON))
+ // actually it is semi invalid could become bad request - not strict but yet accepted
+ // single element is assumed to be array of single element see {@link MgmtApiConfiguration#configureMessageConverters}
+ mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).contentType(MediaType.APPLICATION_JSON).content("{\"id\":44456}"))
.andDo(MockMvcResultPrinter.print())
- .andExpect(status().isBadRequest());
+ .andExpect(status().isNotFound());
- mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("[{\"id\":44456}]")
- .contentType(MediaType.APPLICATION_JSON))
+ mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).contentType(MediaType.APPLICATION_JSON).content("[{\"id\":44456}]"))
.andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/pom.xml b/hawkbit-mgmt/hawkbit-mgmt-server/pom.xml
index e9dc7220b7..a6cd68b1f0 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/pom.xml
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/pom.xml
@@ -32,6 +32,11 @@
${project.version}
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+
+
com.h2database
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java b/hawkbit-mgmt/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java
index b486650660..97d0c31328 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java
@@ -14,9 +14,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.boot.autoconfigure.web.ServerProperties;
-import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
-import org.springframework.boot.web.servlet.error.ErrorAttributes;
+import org.springframework.boot.autoconfigure.web.WebProperties;
+import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
+import org.springframework.boot.webmvc.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
* A new {@link ErrorController}.
*
* @param errorAttributes the error attributes
- * @param serverProperties configuration properties
+ * @param webProperties web configuration properties
*/
- public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) {
- super(errorAttributes, serverProperties.getError());
+ public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
+ super(errorAttributes, webProperties.getError());
}
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
diff --git a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
index f4008fac4c..42b6b1cbda 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
+++ b/hawkbit-mgmt/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java
@@ -11,6 +11,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -59,11 +60,13 @@ void validateCorsRequest() throws Exception {
.getContentAsString();
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
- final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(
- MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden())
+ performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST)
+ // TODO (Spring Boot 4 Migration): Since 4.0 at lest test returns 200 for not found - not going via CORS
+ // before it was 403 + INVALID_CORS_REQUEST
+// .andExpect(status().isForbidden()).andExpect(content().string(INVALID_CORS_REQUEST)) // Spring Boot 3
+ .andExpect(content().string(""))
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
.getResponse().getContentAsString();
- assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
}
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {
diff --git a/hawkbit-mgmt/hawkbit-mgmt-starter/pom.xml b/hawkbit-mgmt/hawkbit-mgmt-starter/pom.xml
index 690cd867c2..37f6f60c2e 100644
--- a/hawkbit-mgmt/hawkbit-mgmt-starter/pom.xml
+++ b/hawkbit-mgmt/hawkbit-mgmt-starter/pom.xml
@@ -53,7 +53,7 @@
org.springframework.boot
- spring-boot-starter-web
+ spring-boot-starter-webmvcorg.springframework.boot
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 f3c17e92d3..2ad2bb66e2 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
@@ -109,14 +109,12 @@ SecurityFilterChain filterChainREST(
// could be used for instance to set authentication provider
// Note: implementation of the customizer shall always take in account what is the already set by the hawkBit
@Autowired(required = false) @Qualifier("hawkbitHttpSecurityCustomizer") final Customizer httpSecurityCustomizer,
- final SystemManagement systemManagement) throws Exception {
+ final SystemManagement systemManagement) {
http
.securityMatcher(MgmtRestConstants.BASE_REST_MAPPING + "/**", MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
.authorizeHttpRequests(amrmRegistry -> amrmRegistry
- .requestMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
- .hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
- .anyRequest()
- .authenticated())
+ .requestMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**").hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
+ .anyRequest().authenticated())
.anonymous(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.addFilterAfter(
diff --git a/hawkbit-monolith/hawkbit-update-server/pom.xml b/hawkbit-monolith/hawkbit-update-server/pom.xml
index c042c9edb8..c08fa7753b 100644
--- a/hawkbit-monolith/hawkbit-update-server/pom.xml
+++ b/hawkbit-monolith/hawkbit-update-server/pom.xml
@@ -32,6 +32,11 @@
${project.version}
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+
+
com.h2database
@@ -58,9 +63,14 @@
+
+
+
+
+
- org.springframework.security
- spring-security-test
+ org.springframework.boot
+ spring-boot-starter-security-testtest
diff --git a/hawkbit-monolith/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java b/hawkbit-monolith/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java
index 5ea1fd3403..57f53d21c1 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java
+++ b/hawkbit-monolith/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java
@@ -14,9 +14,9 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import org.springframework.boot.autoconfigure.web.ServerProperties;
-import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
-import org.springframework.boot.web.servlet.error.ErrorAttributes;
+import org.springframework.boot.autoconfigure.web.WebProperties;
+import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
+import org.springframework.boot.webmvc.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
* A new {@link ErrorController}.
*
* @param errorAttributes the error attributes
- * @param serverProperties configuration properties
+ * @param webProperties web configuration properties
*/
- public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) {
- super(errorAttributes, serverProperties.getError());
+ public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
+ super(errorAttributes, webProperties.getError());
}
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
diff --git a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
index 4259aec3ee..79d7a961a8 100644
--- a/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
+++ b/hawkbit-monolith/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java
@@ -11,6 +11,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -21,6 +22,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.test.web.servlet.ResultActions;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
/**
* Feature: Integration Test - Security
@@ -59,11 +61,13 @@ void validateCorsRequest() throws Exception {
.getContentAsString();
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
- final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(
- MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden())
+ performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST)
+ // TODO (Spring Boot 4 Migration): Since 4.0 at lest test returns 200 for not found - not going via CORS
+ // before it was 403 + INVALID_CORS_REQUEST
+// .andExpect(status().isForbidden()).andExpect(content().string(INVALID_CORS_REQUEST)) // Spring Boot 3
+ .andExpect(content().string(""))
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
.getResponse().getContentAsString();
- assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
}
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {
diff --git a/hawkbit-ql-jpa/pom.xml b/hawkbit-ql-jpa/pom.xml
index 5c3a5a2fe7..e10c25ec7b 100644
--- a/hawkbit-ql-jpa/pom.xml
+++ b/hawkbit-ql-jpa/pom.xml
@@ -22,6 +22,11 @@
hawkBit :: Query Language :: JPA
+
+ jakarta.persistence
+ jakarta.persistence-api
+
+
org.eclipse.persistenceorg.eclipse.persistence.jpa
@@ -34,19 +39,9 @@
true
-
- jakarta.persistence
- jakarta.persistence-api
- org.springframework.bootspring-boot-starter-data-jpa
-
-
- org.hibernate.orm
- hibernate-core
-
- true
@@ -56,6 +51,11 @@
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa-test
+ test
+ com.h2databaseh2
diff --git a/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/utils/HibernateUtils.java b/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/utils/HibernateUtils.java
index 4df2a31516..e10a9a6ba8 100644
--- a/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/utils/HibernateUtils.java
+++ b/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/utils/HibernateUtils.java
@@ -22,8 +22,10 @@
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.spi.QueryEngine;
+import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
-import org.hibernate.query.sqm.internal.QuerySqmImpl;
+import org.hibernate.query.sqm.internal.DomainParameterXref;
+import org.hibernate.query.sqm.internal.SqmQueryImpl;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
import org.hibernate.query.sqm.sql.SqmTranslation;
@@ -33,6 +35,7 @@
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
+import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.MutationStatement;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.SelectStatement;
@@ -60,14 +63,14 @@ public static String toSql(final Query query) {
throw new UnsupportedOperationException("SqmTranslatorFactory resolver is not available");
}
- final QuerySqmImpl> hqlQuery = query.unwrap(QuerySqmImpl.class);
- final SessionFactoryImplementor factory = hqlQuery.getSessionFactory();
+ final SqmQueryImpl> hqlQuery = query.unwrap(SqmQueryImpl.class);
+ final SessionFactoryImplementor sessionFactory = hqlQuery.getSessionFactory();
final SharedSessionContractImplementor session = hqlQuery.getSession();
- final SessionFactoryImplementor sessionFactory = session.getFactory();
+ final SqlAstCreationContext sqlAstCreationContext = (SqlAstCreationContext)session.getFactory();
final SqmTranslatorFactory sqmTranslatorFactory;
try {
- sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(factory.getQueryEngine());
+ sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(sessionFactory.getQueryEngine());
} catch (final IllegalAccessException | InvocationTargetException e) {
throw new UnsupportedOperationException("Can't create SqmTranslatorFactory", e);
}
@@ -76,19 +79,20 @@ public static String toSql(final Query query) {
hqlQuery.getSqmStatement() instanceof SqmSelectStatement> selectStatement
? sqmTranslatorFactory.createSelectTranslator(selectStatement,
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
- hqlQuery.getLoadQueryInfluencers(), sessionFactory, false)
+ hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext, false)
: sqmTranslatorFactory.createMutationTranslator((SqmDmlStatement>) hqlQuery.getSqmStatement(),
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
- hqlQuery.getLoadQueryInfluencers(), sessionFactory);
+ hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext);
final SqmTranslation extends Statement> sqmTranslation = sqmSelectTranslator.translate();
- final SqlAstTranslatorFactory sqlAstTranslatorFactory = factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
+ final SqlAstTranslatorFactory sqlAstTranslatorFactory = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
final Map, Map, List>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
hqlQuery.getDomainParameterXref(), sqmTranslation::getJdbcParamsBySqmParam);
- final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(hqlQuery.getQueryParameterBindings(),
- hqlQuery.getDomainParameterXref(), jdbcParamsXref, factory.getRuntimeMetamodels().getMappingMetamodel(),
- sqmSelectTranslator.getFromClauseAccess()::findTableGroup, new SqmParameterMappingModelResolutionAccess() {
+ final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
+ (QueryParameterBindings) hqlQuery.getQueryParameterBindings(),
+ (DomainParameterXref) hqlQuery.getDomainParameterXref(), jdbcParamsXref, /*sessionFactory.getRuntimeMetamodels().getMappingMetamodel(),
+ sqmSelectTranslator.getFromClauseAccess()::findTableGroup,*/ new SqmParameterMappingModelResolutionAccess() {
@Override
@SuppressWarnings("unchecked")
@@ -97,9 +101,9 @@ public MappingModelExpressible getResolvedMappingModelType(final SqmParam
}
}, hqlQuery.getSession());
return (sqmTranslation.getSqlAst() instanceof SelectStatement selectStatement
- ? sqlAstTranslatorFactory.buildSelectTranslator(factory, selectStatement)
+ ? sqlAstTranslatorFactory.buildSelectTranslator(sessionFactory, selectStatement)
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions())
- : sqlAstTranslatorFactory.buildMutationTranslator(factory, (MutationStatement) sqmTranslation.getSqlAst())
+ : sqlAstTranslatorFactory.buildMutationTranslator(sessionFactory, (MutationStatement) sqmTranslation.getSqlAst())
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions()))
.getSqlString();
}
diff --git a/hawkbit-ql-jpa/src/test/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilderTest.java b/hawkbit-ql-jpa/src/test/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilderTest.java
index 0a02f4187e..619ea3c7be 100644
--- a/hawkbit-ql-jpa/src/test/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilderTest.java
+++ b/hawkbit-ql-jpa/src/test/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilderTest.java
@@ -26,12 +26,11 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
-import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.data.jpa.domain.Specification;
@SuppressWarnings("java:S5961") // complex check because the matter is very complex
-@DataJpaTest(properties = { "spring.jpa.database=H2" }, excludeAutoConfiguration = { FlywayAutoConfiguration.class })
+@DataJpaTest(properties = "spring.jpa.database=H2")
@EnableAutoConfiguration
@Slf4j
class SpecificationBuilderTest {
diff --git a/hawkbit-repository/hawkbit-repository-api/pom.xml b/hawkbit-repository/hawkbit-repository-api/pom.xml
index 3dceb304d7..9b2139070b 100644
--- a/hawkbit-repository/hawkbit-repository-api/pom.xml
+++ b/hawkbit-repository/hawkbit-repository-api/pom.xml
@@ -34,9 +34,10 @@
- com.fasterxml.jackson.core
- jackson-annotations
+ org.springframework.data
+ spring-data-commons
+
com.cronutilscron-utils
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
index d7e256bc22..925eef65ba 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java
@@ -49,7 +49,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
import org.springframework.security.access.prepost.PreAuthorize;
/**
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractRemoteEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractRemoteEvent.java
index 403b4ff16f..5487ef43cb 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractRemoteEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractRemoteEvent.java
@@ -23,7 +23,7 @@
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class AbstractRemoteEvent extends ApplicationEvent {
- private final String id;
+ private String id; // not a final - jackson 3 doesn't override finals
// for serialization libs like jackson
protected AbstractRemoteEvent() {
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java
index fd8e43e70a..84d5038c62 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java
@@ -12,6 +12,7 @@
import java.io.Serial;
import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.Action;
@@ -34,7 +35,8 @@ public class MultiActionAssignEvent extends MultiActionEvent {
* @param tenant tenant the event is scoped to
* @param actions the actions of the deployment action
*/
- public MultiActionAssignEvent(String tenant, List actions) {
+ @JsonIgnore
+ public MultiActionAssignEvent(final String tenant, final List actions) {
super(tenant, actions);
}
}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java
index 68bb2abedf..c6c4b40c90 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java
@@ -12,6 +12,7 @@
import java.io.Serial;
import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.Action;
@@ -26,7 +27,8 @@ public class MultiActionCancelEvent extends MultiActionEvent {
@Serial
private static final long serialVersionUID = 1L;
- public MultiActionCancelEvent(String tenant, List actions) {
+ @JsonIgnore
+ public MultiActionCancelEvent(final String tenant, final List actions) {
super(tenant, actions);
}
}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEvent.java
index 2c0270de3b..4c1ce58fc3 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteIdEvent.java
@@ -25,7 +25,7 @@
* Note: it implements {@link org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent} methods but in order
* to be really include in the cache eviction process the subclasses must declare that it implements that interface.
*/
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@@ -34,13 +34,13 @@ public abstract class RemoteIdEvent extends RemoteTenantAwareEvent {
@Serial
private static final long serialVersionUID = 1L;
- private Long entityId;
- private String entityClass;
+ private final Long entityId;
+ private final String entityClass;
protected RemoteIdEvent(final String tenant, final Long entityId, final Class extends TenantAwareBaseEntity> entityClass) {
super(tenant, entityId);
- this.entityClass = entityClass.getName();
this.entityId = entityId;
+ this.entityClass = entityClass.getName();
}
public String getCacheName() {
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEvent.java
index 8d494547b6..a624ff356b 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEvent.java
@@ -24,7 +24,7 @@
* distributed events. All the necessary information of distributing events to
* other nodes.
*/
-@NoArgsConstructor(access = AccessLevel.PROTECTED) // for serialization libs like jackson
+@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) // for serialization libs like jackson
@Getter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@@ -33,7 +33,7 @@ public class RemoteTenantAwareEvent extends AbstractRemoteEvent implements Tenan
@Serial
private static final long serialVersionUID = 1L;
- private String tenant;
+ private final String tenant;
/**
* Constructor.
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
index 9016e596ca..82f507d36f 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
@@ -16,7 +16,7 @@
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.eclipse.hawkbit.repository.model.Action;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Defines the remote event of creating a new {@link Action}.
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
index ed49319cb8..a7a509d1e0 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
@@ -24,6 +25,7 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre
@Serial
private static final long serialVersionUID = 2L;
+ @JsonIgnore
public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
super(action, targetId, rolloutId, rolloutGroupId);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
index fa566ab4e9..88d5446e57 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
@@ -24,6 +25,7 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd
@Serial
private static final long serialVersionUID = 2L;
+ @JsonIgnore
public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
super(action, targetId, rolloutId, rolloutGroupId);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/DistributionSetCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/DistributionSetCreatedEvent.java
index d0ee0c1ca0..bfe90f8fd2 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/DistributionSetCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/DistributionSetCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -24,6 +25,7 @@ public class DistributionSetCreatedEvent extends RemoteEntityEvent implements E
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public RolloutCreatedEvent(final Rollout rollout) {
super(rollout);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupCreatedEvent.java
index ede407a237..ce2be47dd2 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -25,6 +26,7 @@ public class RolloutGroupCreatedEvent extends AbstractRolloutGroupEvent implemen
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
super(rolloutGroup, rolloutId);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupUpdatedEvent.java
index 5cc6d096e7..74bc24feaa 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutGroupUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -24,6 +25,7 @@ public class RolloutGroupUpdatedEvent extends AbstractRolloutGroupEvent implemen
@Serial
private static final long serialVersionUID = 2L;
+ @JsonIgnore
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
super(rolloutGroup, rolloutId);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutUpdatedEvent.java
index 9f1bc0e608..a569191a65 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/RolloutUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -24,6 +25,7 @@ public class RolloutUpdatedEvent extends RemoteEntityEvent implements E
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public RolloutUpdatedEvent(final Rollout rollout) {
super(rollout);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleCreatedEvent.java
index 453ad669e4..b8e35bb46e 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -24,6 +25,7 @@ public class SoftwareModuleCreatedEvent extends RemoteEntityEvent implements Ent
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetCreatedEvent(final Target target) {
super(target);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetFilterQueryCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetFilterQueryCreatedEvent.java
index dc16fef014..0a0e58421a 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetFilterQueryCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetFilterQueryCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -24,6 +25,7 @@ public class TargetFilterQueryCreatedEvent extends RemoteEntityEvent implemen
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetTagCreatedEvent(final TargetTag targetTag) {
super(targetTag);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTagUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTagUpdatedEvent.java
index 160d62b7fc..ad0184e48a 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTagUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTagUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetTag;
@@ -24,6 +25,7 @@ public class TargetTagUpdatedEvent extends RemoteEntityEvent implemen
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetTagUpdatedEvent(final TargetTag targetTag) {
super(targetTag);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeCreatedEvent.java
index da15b2820c..13faa20949 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType;
@@ -25,6 +26,7 @@ public class TargetTypeCreatedEvent extends RemoteEntityEvent implem
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetTypeCreatedEvent(final TargetType targetType) {
super(targetType);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeUpdatedEvent.java
index 92d3d88869..08fbaf6bfa 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetTypeUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType;
@@ -25,6 +26,7 @@ public class TargetTypeUpdatedEvent extends RemoteEntityEvent implem
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetTypeUpdatedEvent(final TargetType targetType) {
super(targetType);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetUpdatedEvent.java
index 83e32a0e4a..ae4cb822a5 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetUpdatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Target;
@@ -24,6 +25,7 @@ public class TargetUpdatedEvent extends RemoteEntityEvent implements Ent
@Serial
private static final long serialVersionUID = 1L;
+ @JsonIgnore
public TargetUpdatedEvent(final Target target) {
super(target);
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TenantConfigurationCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TenantConfigurationCreatedEvent.java
index dbc762b740..c1df003ccc 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TenantConfigurationCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/TenantConfigurationCreatedEvent.java
@@ -11,6 +11,7 @@
import java.io.Serial;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
@@ -24,6 +25,7 @@ public class TenantConfigurationCreatedEvent extends RemoteEntityEvent
- org.springframework
- spring-context-support
+ org.springframework.cloud
+ spring-cloud-starter-stream-rabbit
+
io.protostuffprotostuff-core
@@ -42,9 +43,5 @@
protostuff-runtimetrue
-
- org.springframework.cloud
- spring-cloud-starter-stream-rabbit
-
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonConfiguration.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonConfiguration.java
new file mode 100644
index 0000000000..edf7441637
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2025 Contributors to the Eclipse Foundation
+ *
+ * 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.event;
+
+import org.eclipse.hawkbit.HawkbitAutoConfiguration;
+import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * The {@link EventJacksonConfiguration} adds to the {@link tools.jackson.databind.json.JsonMapper} configuration
+ * (already modified by the {@link HawkbitAutoConfiguration}) the event subtypes defined in {@link EventType}
+ */
+@Configuration
+@Import(HawkbitAutoConfiguration.class)
+public class EventJacksonConfiguration {
+
+ @Bean
+ JsonMapperBuilderCustomizer jsonMapperBuilderCustomizer() {
+ return jsonMapperBuilder -> jsonMapperBuilder.registerSubtypes(EventType.getNamedTypes());
+ }
+}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonMessageConverter.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonMessageConverter.java
index cfd2b98d29..35046719c3 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonMessageConverter.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventJacksonMessageConverter.java
@@ -9,31 +9,35 @@
*/
package org.eclipse.hawkbit.event;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
-import org.springframework.messaging.converter.MappingJackson2MessageConverter;
+import org.springframework.messaging.converter.JacksonJsonMessageConverter;
import org.springframework.util.MimeType;
+import tools.jackson.databind.json.JsonMapper;
-public class EventJacksonMessageConverter extends MappingJackson2MessageConverter {
+public class EventJacksonMessageConverter extends JacksonJsonMessageConverter {
public static final MimeType APPLICATION_REMOTE_EVENT_JSON = new MimeType("application", "remote-event-json");
-
- public EventJacksonMessageConverter() {
- super(APPLICATION_REMOTE_EVENT_JSON);
- ObjectMapper objectMapper = new ObjectMapper();
- EventType.getNamedTypes().forEach(objectMapper::registerSubtypes);
- setObjectMapper(objectMapper);
+ public EventJacksonMessageConverter(final JsonMapper mapper) {
+ super(mapper, APPLICATION_REMOTE_EVENT_JSON);
}
@Override
- protected Object convertToInternal(final Object payload, final MessageHeaders headers, final Object conversionHint) {
+ @SuppressWarnings("java:S1185") // intentionally override in order to extend visibility
+ @NullMarked
+ @Nullable
+ protected Object convertToInternal(final Object payload, @Nullable final MessageHeaders headers, @Nullable final Object conversionHint) {
return super.convertToInternal(payload, headers, conversionHint);
}
@Override
- protected Object convertFromInternal(final Message> message, final Class> targetClass, final Object conversionHint) {
+ @SuppressWarnings("java:S1185") // intentionally override in order to extend visibility
+ @NullMarked
+ @Nullable
+ protected Object convertFromInternal(final Message> message, final Class> targetClass, @Nullable final Object conversionHint) {
return super.convertFromInternal(message, targetClass, conversionHint);
}
-}
+}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventProtoStuffMessageConverter.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventProtoStuffMessageConverter.java
index 2b8d2504c8..48532e50f0 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventProtoStuffMessageConverter.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventProtoStuffMessageConverter.java
@@ -17,6 +17,8 @@
import io.protostuff.runtime.RuntimeSchema;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter;
@@ -46,18 +48,20 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
public static final MimeType APPLICATION_BINARY_PROTOSTUFF = new MimeType("application", "binary+protostuff");
private static final int HEADER_LENGTH_PREFIX_SIZE = 4;
-
public EventProtoStuffMessageConverter() {
super(APPLICATION_BINARY_PROTOSTUFF);
}
@Override
+ @NullMarked
protected boolean supports(final Class> aClass) {
return AbstractRemoteEvent.class.isAssignableFrom(aClass);
}
@Override
- protected Object convertFromInternal(final Message> message, final Class> targetClass, final Object conversionHint) {
+ @NullMarked
+ @Nullable
+ protected Object convertFromInternal(final Message> message, final Class> targetClass, @Nullable final Object conversionHint) {
final Object objectPayload = message.getPayload();
if (objectPayload instanceof byte[] payload) {
final byte[] clazzHeader = extractClazzHeader(payload);
@@ -112,7 +116,6 @@ private static byte[] extractContent(final byte[] payload) {
return content;
}
-
private static EventType readClassHeader(final byte[] typeInformation) {
final Schema schema = RuntimeSchema.getSchema(EventType.class);
final EventType deserializedType = schema.newMessage();
@@ -134,8 +137,7 @@ private static byte[] writeClassHeader(final Class> clazz) {
throw new MessageConversionException("Missing EventType for given class : " + clazz);
}
- @SuppressWarnings("unchecked")
- final Schema
+
+ org.eclipse.persistence
+ org.eclipse.persistence.jpa
+ ${eclipselink.version}
+
+
io.micrometermicrometer-coretrue
-
-
+ org.hibernate.orm
- hibernate-jpamodelgen
+ hibernate-processortrue
-
-
- org.eclipse.persistence
- org.eclipse.persistence.jpa
- ${eclipselink.version}
-
@@ -71,13 +70,6 @@
org.eclipse.hawkbit.repository.jpa.model
-
-
- org.eclipse.persistence
- org.eclipse.persistence.jpa
- ${eclipselink.version}
-
-
diff --git a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java
index 0c3996a017..2ffdf85e0f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/HawkbitEclipseLinkJpaDialect.java
@@ -17,7 +17,7 @@
import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
-import org.springframework.lang.NonNull;
+import org.jspecify.annotations.NonNull;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
diff --git a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
index 283481ee26..d0e48b6c26 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
@@ -17,10 +17,10 @@
import lombok.Data;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
-import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jpa.autoconfigure.JpaBaseConfiguration;
+import org.springframework.boot.jpa.autoconfigure.JpaProperties;
+import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -38,7 +38,7 @@
public class JpaConfiguration extends JpaBaseConfiguration {
@Data
- @ConfigurationProperties // predix is "/" intentionally
+ @ConfigurationProperties // prefix is "/" intentionally
protected static class Properties {
private final Map eclipselink = new HashMap<>();
diff --git a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
index 28f67b764a..3e5c8f8cd4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-eclipselink/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
@@ -42,9 +42,9 @@
*
*
The Spring property spring.jpa.properties.eclipselink.profiler=PerformanceMonitor shall be set - enables Eclipselink statistics
* collecting
- *
By default the periodic stdout log is disabled by setting hawkbit.jpa.statistics.dump-period-ms=9223372036854775807 (Long.MAX_VALUE) -
+ *
By default, the periodic stdout log is disabled by setting hawkbit.jpa.statistics.dump-period-ms=9223372036854775807 (Long.MAX_VALUE) -
* i.e. effectively never. If log is required it should be set to the required period
- *
The MeterRegistry shall be registered available - e.g. include org.springframework.boot:spring-boot-actuator-autoconfigure
+ *
The MeterRegistry shall be registered and available
*
(?) When using in test the metrics MAYBE shall be enabled with @AutoConfigureObservability(tracing = false)
*
*
diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/pom.xml b/hawkbit-repository/hawkbit-repository-jpa-flyway/pom.xml
index db3c502395..8c304280d0 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-flyway/pom.xml
+++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/pom.xml
@@ -24,16 +24,7 @@
org.springframework.boot
- spring-boot
-
-
- org.springframework.boot
- spring-boot-autoconfigure
-
-
-
- org.flywaydb
- flyway-core
+ spring-boot-starter-flyway
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java
index b1589fcd3a..1743db7be8 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/java/org/eclipse/hawkbit/autoconfigure/repository/jpa/flyway/HawkbitFlywayAutoConfiguration.java
@@ -10,7 +10,7 @@
package org.eclipse.hawkbit.autoconfigure.repository.jpa.flyway;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
+import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/H2/V1_12_36__spring_boot_4__H2.sql b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/H2/V1_12_36__spring_boot_4__H2.sql
new file mode 100644
index 0000000000..f7d96f1393
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/H2/V1_12_36__spring_boot_4__H2.sql
@@ -0,0 +1 @@
+ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER TIMESTAMP NOT NULL;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/MYSQL/V1_12_36__spring_boot_4__MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/MYSQL/V1_12_36__spring_boot_4__MYSQL.sql
new file mode 100644
index 0000000000..8aa843e42e
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/MYSQL/V1_12_36__spring_boot_4__MYSQL.sql
@@ -0,0 +1 @@
+ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER DATETIME(6) NOT NULL;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/POSTGRESQL/V1_12_37__spring_boot_4__POSTGRESQL.sql b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/POSTGRESQL/V1_12_37__spring_boot_4__POSTGRESQL.sql
new file mode 100644
index 0000000000..f7d96f1393
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-jpa-flyway/src/main/resources/db/migration/POSTGRESQL/V1_12_37__spring_boot_4__POSTGRESQL.sql
@@ -0,0 +1 @@
+ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER TIMESTAMP NOT NULL;
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa-hibernate/pom.xml b/hawkbit-repository/hawkbit-repository-jpa-hibernate/pom.xml
index 5aa6942b1d..950df9b77e 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-hibernate/pom.xml
+++ b/hawkbit-repository/hawkbit-repository-jpa-hibernate/pom.xml
@@ -43,10 +43,9 @@
true
-
-
+ org.hibernate.orm
- hibernate-jpamodelgen
+ hibernate-processortrue
diff --git a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
index b232c3bbf9..f6bd7b9e70 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaConfiguration.java
@@ -30,10 +30,10 @@
import org.hibernate.jpa.boot.spi.IntegratorProvider;
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
import org.springframework.beans.factory.ObjectProvider;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
-import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jpa.autoconfigure.JpaBaseConfiguration;
+import org.springframework.boot.jpa.autoconfigure.JpaProperties;
+import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
diff --git a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
index 3a8018c91b..0219c5f556 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/Statistics.java
@@ -27,8 +27,8 @@
*
*
The Spring property spring.jpa.properties.hibernate.generate_statistics=true shall be set - enables Hibernate statistics
* collecting
- *
If don't need periodic log (Slf4J) set logging.level.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=WARN
- *
The MeterRegistry shall be registered available - e.g. include org.springframework.boot:spring-boot-actuator-autoconfigure
+ *
If you don't need periodic log (Slf4J) set logging.level.org.hibernate.engine.internal.StatisticalLoggingSessionEventListener=WARN
+ *
The MeterRegistry shall be registered and available
*
Hibernate reporting to micrometer shall be enabled - include org.hibernate.orm:hibernate-micrometer
*
(?) When using in test the metrics MAYBE shall be enabled with @AutoConfigureObservability(tracing = false)
*
diff --git a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantIdentifier.java b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantIdentifier.java
index 4b2e10ff78..7b4592420e 100644
--- a/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantIdentifier.java
+++ b/hawkbit-repository/hawkbit-repository-jpa-hibernate/src/main/java/org/eclipse/hawkbit/repository/jpa/TenantIdentifier.java
@@ -13,10 +13,9 @@
import org.eclipse.hawkbit.context.AccessContext;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
-import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
/**
- * {@link CurrentTenantIdentifierResolver} and {@link HibernatePropertiesCustomizer} that resolves the
+ * {@link CurrentTenantIdentifierResolver} and {@link org.springframework.boot.hibernate.autoconfigure.HibernatePropertiesCustomizer} that resolves the
* {@link AccessContext#tenant()} for hibernate.
*/
class TenantIdentifier implements CurrentTenantIdentifierResolver {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/pom.xml b/hawkbit-repository/hawkbit-repository-jpa/pom.xml
index 4d5d645f6a..d751e278d5 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/pom.xml
+++ b/hawkbit-repository/hawkbit-repository-jpa/pom.xml
@@ -63,13 +63,6 @@
org.eclipse.hawkbit.repository.jpa.model
-
-
- org.eclipse.persistence
- org.eclipse.persistence.jpa
- ${eclipselink.version}
-
-
@@ -101,44 +94,25 @@
${project.version}
-
- io.micrometer
- micrometer-core
- true
-
-
-
-
- org.hibernate.orm
- hibernate-jpamodelgen
- true
-
-
-
- org.springframework
- spring-core
-
-
- org.springframework.security
- spring-security-core
- org.springframework.integrationspring-integration-jdbc
- org.jsoup
- jsoup
-
-
- org.hibernate.validator
- hibernate-validator
+ io.micrometer
+ micrometer-coreorg.apache.commonscommons-collections4
+
+ org.hibernate.orm
+ hibernate-processor
+ true
+
+
org.eclipse.hawkbit
@@ -147,16 +121,14 @@
test
-
org.springframework.boot
- spring-boot-actuator-autoconfigure
+ spring-boot-starter-data-jpa-testtest
-
- org.hibernate.orm
- hibernate-micrometer
+ org.springframework.boot
+ spring-boot-starter-micrometer-metricstest
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 e4ca8d306a..9bf061f885 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
@@ -15,18 +15,27 @@
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
import jakarta.persistence.EntityManager;
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.Nullable;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.jpa.repository.query.JpaQueryMethodFactory;
+import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
+import org.springframework.data.jpa.repository.support.JpaRepositoryFragmentsContributor;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.querydsl.EntityPathResolver;
@@ -35,16 +44,17 @@
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
-import org.springframework.lang.Nullable;
/**
* A {@link TransactionalRepositoryFactoryBeanSupport} extension that uses {@link HawkbitBaseRepository} as base repository and
* proxied repositories in order to convert exceptions to management exceptions.
*/
+@Slf4j
@SuppressWarnings("java:S119") // java:S119 - ID is inherited from TransactionalRepositoryFactoryBeanSupport
public class HawkbitBaseRepositoryFactoryBean, S, ID> extends TransactionalRepositoryFactoryBeanSupport {
private EntityPathResolver entityPathResolver;
+ private JpaRepositoryFragmentsContributor repositoryFragmentsContributor;
private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
private JpaQueryMethodFactory queryMethodFactory;
@@ -65,6 +75,11 @@ public void setEntityPathResolver(final ObjectProvider resol
this.entityPathResolver = resolver.getIfAvailable(() -> SimpleEntityPathResolver.INSTANCE);
}
+ @Autowired
+ public void setRepositoryFragmentsContributor(final ObjectProvider repositoryFragmentsContributor) {
+ this.repositoryFragmentsContributor = repositoryFragmentsContributor.getIfAvailable(() -> JpaRepositoryFragmentsContributor.DEFAULT);
+ }
+
@Autowired
public void setEscapeCharacter(final char escapeCharacter) {
this.escapeCharacter = EscapeCharacter.of(escapeCharacter);
@@ -77,6 +92,45 @@ public void setQueryMethodFactory(@Nullable final JpaQueryMethodFactory factory)
}
}
+ @Autowired
+ public void setQueryMethodFactory(final ObjectProvider resolver) {
+ final JpaQueryMethodFactory factory = resolver.getIfAvailable();
+ if (factory != null) {
+ this.queryMethodFactory = factory;
+ }
+ }
+
+ private @Nullable BeanFactory beanFactory;
+
+ @Override
+ public void setBeanFactory(final BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ super.setBeanFactory(beanFactory);
+ }
+
+ private @Nullable Function<@Nullable BeanFactory, QueryEnhancerSelector> queryEnhancerSelectorSource;
+
+ public void setQueryEnhancerSelectorSource(QueryEnhancerSelector queryEnhancerSelectorSource) {
+ this.queryEnhancerSelectorSource = bf -> queryEnhancerSelectorSource;
+ }
+
+ public void setQueryEnhancerSelector(final Class extends QueryEnhancerSelector> queryEnhancerSelectorType) {
+ queryEnhancerSelectorSource = bf -> {
+ if (bf != null) {
+ final QueryEnhancerSelector selector = bf.getBeanProvider(queryEnhancerSelectorType).getIfAvailable();
+ if (selector != null) {
+ return selector;
+ }
+
+ if (bf instanceof AutowireCapableBeanFactory acbf) {
+ return acbf.createBean(queryEnhancerSelectorType);
+ }
+ }
+
+ return BeanUtils.instantiateClass(queryEnhancerSelectorType);
+ };
+ }
+
@PersistenceContext
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
@@ -85,6 +139,7 @@ public void setEntityManager(final EntityManager entityManager) {
@Override
public void afterPropertiesSet() {
Objects.requireNonNull(entityManager, "EntityManager must not be null");
+ setRepositoryBaseClass(HawkbitBaseRepository.class); // overrides set by properties base class
super.afterPropertiesSet();
}
@@ -107,6 +162,19 @@ protected RepositoryFactorySupport doCreateRepositoryFactory() {
interfaces(jpaRepositoryImplementation.getClass(), new HashSet<>()).toArray(new Class>[0]),
(proxy, method, args) -> {
try {
+ if (args != null) {
+ final Class>[] parameterTypes = method.getParameterTypes();
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (parameterTypes[i] == Specification.class && args[i] == null) {
+ // replaces null specifications with unrestricted specifications (null not accepted since Spring Boott 4.0
+ if (log.isTraceEnabled()) {
+ log.trace("Replace null Specification argument with unrestricted Specification",
+ new Exception("Method " + method + ", arg[" + i + "]"));
+ }
+ args[i] = Specification.unrestricted();
+ }
+ }
+ }
return method.invoke(jpaRepositoryImplementation, args);
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause() == null ? e : e.getCause();
@@ -120,10 +188,13 @@ protected RepositoryFactorySupport doCreateRepositoryFactory() {
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);
-
+ jpaRepositoryFactory.setFragmentsContributor(repositoryFragmentsContributor);
if (queryMethodFactory != null) {
jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
}
+ if (queryEnhancerSelectorSource != null) {
+ jpaRepositoryFactory.setQueryEnhancerSelector(queryEnhancerSelectorSource.apply(beanFactory));
+ }
jpaRepositoryFactory.setRepositoryBaseClass(HawkbitBaseRepository.class);
return jpaRepositoryFactory;
}
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 06bbad913c..b711c06f2e 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
@@ -93,8 +93,8 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.domain.EntityScan;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
+import org.springframework.boot.persistence.autoconfigure.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -109,8 +109,8 @@
import org.springframework.integration.jdbc.lock.LockRepository;
import org.springframework.integration.support.locks.DefaultLockRegistry;
import org.springframework.integration.support.locks.LockRegistry;
-import org.springframework.lang.NonNull;
-import org.springframework.retry.annotation.EnableRetry;
+import org.jspecify.annotations.NonNull;
+import org.springframework.resilience.annotation.EnableResilientMethods;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.authorization.AuthorizationResult;
@@ -128,7 +128,7 @@
@EnableAspectJAutoProxy
@Configuration
@EnableScheduling
-@EnableRetry
+@EnableResilientMethods
@EntityScan("org.eclipse.hawkbit.repository.jpa.model")
@ComponentScan({ "org.eclipse.hawkbit.repository.jpa.management", "org.eclipse.hawkbit.repository.jpa.scheduler" })
@PropertySource("classpath:/hawkbit-jpa-defaults.properties")
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 70d42d5760..32c562c488 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
@@ -11,9 +11,14 @@
import java.util.Optional;
+import jakarta.persistence.criteria.Root;
+
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
+import org.springframework.data.jpa.domain.DeleteSpecification;
+import org.springframework.data.jpa.domain.PredicateSpecification;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.lang.Nullable;
+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.
@@ -49,6 +54,20 @@ default Specification appendAccessRules(final Operation operation, @Nullable
.orElse(specification);
}
+ default 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);
+ }
+
+ default 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);
+ }
+
/**
* Verify if the given {@link Operation} is permitted for the provided entity.
*
@@ -68,6 +87,11 @@ default void assertOperationAllowed(final Operation operation, final Iterable
}
}
+ @Deprecated
+ default PredicateSpecification predicateSpec(final Specification spec) {
+ return (from, cb) -> spec.toPredicate((Root) from, cb.createQuery(), cb);
+ }
+
/**
* Enum to define the perform operation to verify
*/
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessControllerConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessControllerConfiguration.java
index a245e517e9..5ed40429fe 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessControllerConfiguration.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/acm/AccessControllerConfiguration.java
@@ -37,6 +37,8 @@
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -144,13 +146,16 @@ MethodSecurityExpressionHandler methodSecurityExpressionHandler(
final DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler() {
@Override
- public EvaluationContext createEvaluationContext(final Supplier authentication, final MethodInvocation mi) {
+ @NullMarked
+ public EvaluationContext createEvaluationContext(
+ final Supplier extends @Nullable Authentication> authentication, final MethodInvocation mi) {
return super.createEvaluationContext(SingletonSupplier.of(() -> new RawAuthoritiesAuthentication(authentication.get())), mi);
}
@Override
+ @NullMarked
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
- final Authentication authentication, final MethodInvocation mi) {
+ @Nullable final Authentication authentication, final MethodInvocation mi) {
return super.createSecurityExpressionRoot(new RawAuthoritiesAuthentication(authentication), mi);
}
};
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/configuration/Constants.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/configuration/Constants.java
index 71d1d529ac..dde9c14d24 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/configuration/Constants.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/configuration/Constants.java
@@ -11,8 +11,7 @@
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
/**
* A constant class which holds only static constants used within the SP server.
@@ -28,12 +27,13 @@ public final class Constants {
* number.
*/
public static final int MAX_ENTRIES_IN_STATEMENT = 999;
+
/**
- * @see Retryable#maxAttempts()
+ * See {@link Retryable#maxRetries()}
*/
- public static final int TX_RT_MAX = 10;
+ public static final String RETRY_MAX = "${org.eclipse.hawkbit.repository.jpa.retry-max:10}";
/**
- * @see Backoff#delay()
+ * See {@link Retryable#delayString()}
*/
- public static final long TX_RT_DELAY = 100;
+ public static final String RETRY_DELAY = "${org.eclipse.hawkbit.repository.jpa.retry-delay:100}";
}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java
index f4e9820778..255a936796 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryManagement.java
@@ -9,9 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
-
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -47,6 +44,7 @@
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
@@ -56,8 +54,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@@ -131,14 +128,14 @@ protected AbstractJpaRepositoryManagement(final R jpaRepository, final EntityMan
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public T create(final C create) {
return jpaRepository.save(AccessController.Operation.CREATE, jpaEntity(create));
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List create(final Collection create) {
return jpaRepository.saveAll(AccessController.Operation.CREATE, create.stream().map(this::jpaEntity).toList());
}
@@ -189,7 +186,7 @@ public boolean exists(final long id) {
@Override
public long count() {
- return jpaRepository.count(isNotDeleted().orElse(null));
+ return jpaRepository.count(isNotDeleted().orElseGet(Specification::unrestricted));
}
@Override
@@ -210,7 +207,7 @@ public Page findByRsql(final String rsql, final Pageable pageable) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public T update(final U update) {
final T entity = getValid(update.getId());
@@ -225,7 +222,7 @@ public T update(final U update) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
public Map update(final Collection update) {
final Map toUpdate = findAllById(update.stream().map(Identifiable::getId).toList(), true)
@@ -261,14 +258,14 @@ public Map update(final Collection update) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void delete(final long id) {
delete0(List.of(id));
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void delete(final Collection ids) {
delete0(ids);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryWithMetadataManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryWithMetadataManagement.java
index 4fa2c8aaf1..f659a4764f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryWithMetadataManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/AbstractJpaRepositoryWithMetadataManagement.java
@@ -9,9 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
-
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
@@ -28,13 +25,13 @@
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.MetadataSupport;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
+import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.WithMetadata;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
@SuppressWarnings("java:S119") // java:S119 - better self explainable
@@ -76,7 +73,7 @@ protected AbstractJpaRepositoryWithMetadataManagement(final R repository, final
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void createMetadata(final Long id, final String key, final MV value) {
final T jpaEntity = getValid(id);
final Map metadataValueMap = jpaEntity.getMetadata();
@@ -91,7 +88,7 @@ public void createMetadata(final Long id, final String key, final MV value) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void createMetadata(final Long id, final Map metadata) {
final T jpaEntity = getValid(id);
final Map metadataValueMap = jpaEntity.getMetadata();
@@ -126,7 +123,7 @@ public Map getMetadata(final Long id) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void deleteMetadata(final Long id, final String key) {
final T jpaEntity = getValid(id);
final Map metadataValueMap = jpaEntity.getMetadata();
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
index fe8d43bc3b..43d102d64c 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaArtifactManagement.java
@@ -52,8 +52,7 @@
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
@@ -93,8 +92,7 @@ protected JpaArtifactManagement(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Artifact create(final ArtifactUpload artifactUpload) {
if (artifactStorage == null) {
throw new UnsupportedOperationException();
@@ -164,8 +162,7 @@ public ArtifactStream getArtifactStream(final String sha1Hash, final long softwa
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void delete(final long id) {
if (artifactStorage == null) {
throw new UnsupportedOperationException();
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
index 281896032b..cb16e19aa4 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaConfirmationManagement.java
@@ -42,8 +42,7 @@
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -103,8 +102,7 @@ public AutoConfirmationStatus activateAutoConfirmation(final String controllerId
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action confirmAction(final long actionId, final Integer code, final Collection deviceMessages) {
log.trace("Action with id {} confirm request is triggered.", actionId);
final Action action = actionRepository.getById(actionId);
@@ -119,8 +117,7 @@ public Action confirmAction(final long actionId, final Integer code, final Colle
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action denyAction(final long actionId, final Integer code, final Collection deviceMessages) {
log.trace("Action with id {} deny request is triggered.", actionId);
final Action action = actionRepository.getById(actionId);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
index 2f7772512e..a42d1d2689 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaControllerManagement.java
@@ -118,8 +118,7 @@
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
@@ -229,8 +228,7 @@ protected void onActionStatusUpdate(final JpaActionStatus newActionStatus, final
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action addCancelActionStatus(final ActionStatusCreate create) {
final JpaAction action = actionRepository.getById(create.getActionId());
if (!action.isCancelingOrCanceled()) {
@@ -275,8 +273,7 @@ public Map> findTargetVisibleMetaDataBySoftwareModuleI
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public ActionStatus addInformationalActionStatus(final ActionStatusCreate create) {
final JpaAction action = actionRepository.getById(create.getActionId());
assertActionStatusQuota(create, action);
@@ -290,8 +287,7 @@ public ActionStatus addInformationalActionStatus(final ActionStatusCreate create
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action addUpdateActionStatus(final ActionStatusCreate statusCreate) {
return addActionStatus(statusCreate);
}
@@ -334,14 +330,14 @@ public Page findActionStatusByAction(final long actionId, final Pa
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = ConcurrencyFailureException.class, noRetryFor = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, excludes = EntityAlreadyExistsException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address) {
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, null, null);
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = ConcurrencyFailureException.class, noRetryFor = EntityAlreadyExistsException.class, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, excludes = EntityAlreadyExistsException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address, final String name, final String type) {
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, name, type);
}
@@ -425,16 +421,14 @@ public boolean hasTargetArtifactAssigned(final long targetId, final String sha1H
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void registerRetrieved(final long actionId, final String message) {
handleRegisterRetrieved(actionId, message);
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Target updateControllerAttributes(final String controllerId, final Map data, final UpdateMode mode) {
// Constraints on attribute keys & values are not validated by EclipseLink. Hence, they are validated here.
if (data.entrySet().stream().anyMatch(e -> !isAttributeEntryValid(e))) {
@@ -518,7 +512,7 @@ public Action cancelAction(final Action action) {
jpaAction.setStatus(Status.CANCELING);
// document that the status has been retrieved
actionStatusRepository.save(
- new JpaActionStatus(jpaAction, Status.CANCELING, java.lang.System.currentTimeMillis(), "manual cancelation requested"));
+ new JpaActionStatus(jpaAction, Status.CANCELING, System.currentTimeMillis(), "manual cancelation requested"));
final Action saveAction = actionRepository.save(jpaAction);
cancelAssignDistributionSetEvent(jpaAction);
@@ -651,7 +645,7 @@ private Target createTarget(final String controllerId, final URI address, final
jpaTarget.setName((StringUtils.hasText(name) ? name : controllerId));
jpaTarget.setSecurityToken(SecurityTokenGenerator.generateToken());
jpaTarget.setUpdateStatus(TargetUpdateStatus.REGISTERED);
- jpaTarget.setLastTargetQuery(java.lang.System.currentTimeMillis());
+ jpaTarget.setLastTargetQuery(System.currentTimeMillis());
jpaTarget.setAddress(Optional.ofNullable(address).map(URI::toString).orElse(null));
if (StringUtils.hasText(type)) {
@@ -712,7 +706,7 @@ private Void updateLastTargetQueries(final String tenant, final List
Constants.MAX_ENTRIES_IN_STATEMENT);
pollChunks.forEach(chunk -> {
- setLastTargetQuery(tenant, java.lang.System.currentTimeMillis(), chunk);
+ setLastTargetQuery(tenant, System.currentTimeMillis(), chunk);
chunk.forEach(controllerId -> afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetPollEvent(controllerId, tenant))));
});
@@ -773,7 +767,7 @@ private Target updateTarget(final JpaTarget toUpdate, final URI address, final S
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
}
- toUpdate.setLastTargetQuery(java.lang.System.currentTimeMillis());
+ toUpdate.setLastTargetQuery(System.currentTimeMillis());
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(toUpdate)));
return targetRepository.save(toUpdate);
}
@@ -900,7 +894,7 @@ private void handleRegisterRetrieved(final Long actionId, final String message)
// case controller retrieves a action multiple times.
if (resultList.isEmpty() || (Status.RETRIEVED != resultList.get(0)[1])) {
// document that the status has been retrieved
- actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, java.lang.System.currentTimeMillis(), message));
+ actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
// don't change the action status itself in case the action is in
// canceling state otherwise
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 65d66c0015..7a01d33c7c 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
@@ -12,6 +12,7 @@
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -39,6 +40,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.context.AccessContext;
+import org.eclipse.hawkbit.exception.GenericSpServerException;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -88,21 +90,23 @@
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.repository.qfields.ActionFields;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
+import org.springframework.boot.jpa.autoconfigure.JpaProperties;
+import org.springframework.core.retry.RetryException;
+import org.springframework.core.retry.RetryPolicy;
+import org.springframework.core.retry.RetryTemplate;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
+import org.springframework.data.jpa.domain.DeleteSpecification;
+import org.springframework.data.jpa.domain.PredicateSpecification;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
-import org.springframework.retry.backoff.FixedBackOffPolicy;
-import org.springframework.retry.policy.SimpleRetryPolicy;
-import org.springframework.retry.support.RetryTemplate;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
@@ -144,7 +148,8 @@ protected JpaDeploymentManagement(
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties,
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
- final EntityManager entityManager, final PlatformTransactionManager txManager, final JpaProperties jpaProperties) {
+ final EntityManager entityManager, final PlatformTransactionManager txManager, final JpaProperties jpaProperties,
+ @Value(Constants.RETRY_MAX) final long maxRetries, @Value(Constants.RETRY_DELAY) final long delay) {
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
this.distributionSetManagement = distributionSetManagement;
this.targetRepository = targetRepository;
@@ -152,7 +157,11 @@ protected JpaDeploymentManagement(
this.txManager = txManager;
this.database = jpaProperties.getDatabase();
- retryTemplate = createRetryTemplate();
+ retryTemplate = new RetryTemplate(RetryPolicy.builder()
+ .includes(ConcurrencyFailureException.class)
+ .maxRetries(maxRetries)
+ .maxDelay(Duration.ofMillis(delay))
+ .build());
final Consumer maxAssignmentsExceededHandler = maxAssignmentsExceededInfo ->
handleMaxAssignmentsExceeded(
maxAssignmentsExceededInfo.targetId,
@@ -188,8 +197,7 @@ public List offlineAssignedDistributionSets(fin
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action cancelAction(final long actionId) {
return cancelAction0(actionId);
}
@@ -210,7 +218,7 @@ private Action cancelAction0(final long actionId) {
action.setStatus(Status.CANCELING);
// document that the status has been retrieved
- actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, java.lang.System.currentTimeMillis(),
+ actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
final Action saveAction = actionRepository.save(action);
@@ -330,8 +338,7 @@ public List findActiveActionsWithHighestWeight(final String controllerId
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action forceQuitAction(final long actionId) {
return forceQuitAction0(actionId);
}
@@ -352,7 +359,7 @@ private Action forceQuitAction0(final long actionId) {
log.warn("action ({}) was still active and has been force quite.", action);
// document that the status has been retrieved
- actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, java.lang.System.currentTimeMillis(),
+ actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
RepositoryConstants.SERVER_MESSAGE_PREFIX + "A force quit has been performed."));
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
@@ -362,8 +369,7 @@ private Action forceQuitAction0(final long actionId) {
@Override
@Transactional
- @Retryable(retryFor = {
- ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Action forceTargetAction(final long actionId) {
final JpaAction action = actionRepository.findById(actionId)
.map(this::assertTargetUpdateAllowed)
@@ -387,7 +393,11 @@ public void deleteAction(final long actionId) {
@Transactional
public void deleteActionsByRsql(final String rsql) {
log.info("Deleting actions matching rsql {}", rsql);
- actionRepository.delete(QLSupport.getInstance().buildSpec(rsql, ActionFields.class));
+ actionRepository.delete(DeleteSpecification.where(predicateSpec(QLSupport.getInstance().buildSpec(rsql, ActionFields.class))));
+ }
+ @Deprecated
+ static PredicateSpecification predicateSpec(final Specification spec) {
+ return (from, cb) -> spec.toPredicate((Root) from, cb.createQuery(), cb);
}
@Override
@@ -420,8 +430,7 @@ public void deleteOldestTargetActions(final String controllerId, final int keepL
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void cancelInactiveScheduledActionsForTargets(final List targetIds) {
if (!isMultiAssignmentsEnabled()) {
targetRepository.getAccessController().ifPresent(v -> {
@@ -626,20 +635,6 @@ private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeStrin
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database, QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
}
- private static RetryTemplate createRetryTemplate() {
- final RetryTemplate template = new RetryTemplate();
-
- final FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
- backOffPolicy.setBackOffPeriod(Constants.TX_RT_DELAY);
- template.setBackOffPolicy(backOffPolicy);
-
- final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
- Constants.TX_RT_MAX, Collections.singletonMap(ConcurrencyFailureException.class, true));
- template.setRetryPolicy(retryPolicy);
-
- return template;
- }
-
private List assignDistributionSets(
final List deploymentRequests, final String actionMessage, final AbstractDsAssignmentStrategy strategy) {
final List validatedRequests = validateAndFilterRequestForAssignments(deploymentRequests);
@@ -741,8 +736,11 @@ private List filterByTargetUpdatable(final List targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
- return retryTemplate.execute(retryContext ->
- assignDistributionSetToTargets(dsId, targetsWithActionType, actionMessage, assignmentStrategy));
+ try {
+ return retryTemplate.execute(() -> assignDistributionSetToTargets(dsId, targetsWithActionType, actionMessage, assignmentStrategy));
+ } catch (final RetryException e) {
+ throw new GenericSpServerException(e);
+ }
}
/**
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
index b9d9ee29cc..c2a675e5c2 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java
@@ -9,8 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList;
@@ -41,6 +39,7 @@
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
+import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
@@ -62,8 +61,7 @@
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@@ -195,7 +193,7 @@ public JpaDistributionSet getWithDetails(final long id) {
// implicitly lock a distribution set if not already locked and implicit lock is enabled and not to skip
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public boolean shouldLockImplicitly(final DistributionSet distributionSet) {
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
if (jpaDistributionSet.isLocked()) {
@@ -227,7 +225,7 @@ public boolean shouldLockImplicitly(final DistributionSet distributionSet) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSet lock(final DistributionSet distributionSet) {
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
if (distributionSet.isLocked()) {
@@ -244,7 +242,7 @@ public JpaDistributionSet lock(final DistributionSet distributionSet) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSet unlock(final DistributionSet distributionSet) {
final JpaDistributionSet jpaDistributionSet = toJpaDistributionSet(distributionSet);
if (jpaDistributionSet.isLocked()) {
@@ -265,7 +263,7 @@ public JpaDistributionSet invalidate(final DistributionSet distributionSet) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSet assignSoftwareModules(final long id, final Collection softwareModuleId) {
final JpaDistributionSet set = getValid0(id);
assertSoftwareModuleQuota(id, softwareModuleId.size());
@@ -283,7 +281,7 @@ public JpaDistributionSet assignSoftwareModules(final long id, final Collection<
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSet unassignSoftwareModule(final long id, final long moduleId) {
final JpaDistributionSet set = getValid0(id);
@@ -295,7 +293,7 @@ public JpaDistributionSet unassignSoftwareModule(final long id, final long modul
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List assignTag(final Collection ids, final long dsTagId) {
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
if (distributionSet.getTags().contains(tag)) {
@@ -309,7 +307,7 @@ public List assignTag(final Collection ids, final long
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List unassignTag(final Collection ids, final long dsTagId) {
return updateTag(ids, dsTagId, (tag, distributionSet) -> {
if (distributionSet.getTags().contains(tag)) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTypeManagement.java
index fdf22c806b..4ecfd9cd5f 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTypeManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetTypeManagement.java
@@ -9,9 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
-
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@@ -24,6 +21,7 @@
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
@@ -40,8 +38,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.cache.Cache;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -71,7 +68,7 @@ protected JpaDistributionSetTypeManagement(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void delete(final long id) {
final JpaDistributionSetType toDelete = jpaRepository.getById(id);
@@ -103,21 +100,21 @@ public Optional findByKey(final String key) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSetType assignOptionalSoftwareModuleTypes(final long id, final Collection softwareModulesTypeIds) {
return assignSoftwareModuleTypes(id, softwareModulesTypeIds, false);
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSetType assignMandatorySoftwareModuleTypes(final long id, final Collection softwareModuleTypeIds) {
return assignSoftwareModuleTypes(id, softwareModuleTypeIds, true);
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaDistributionSetType unassignSoftwareModuleType(final long id, final long softwareModuleTypeId) {
final JpaDistributionSetType type = jpaRepository.getById(id);
checkDistributionSetTypeNotAssigned(id);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java
index 391838cf4c..52dfe46e87 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java
@@ -94,8 +94,7 @@
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@@ -185,8 +184,7 @@ public long countByDistributionSetIdAndRolloutIsStoppable(final long setId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout create(
final Create rollout, final int amountGroup, final boolean confirmationRequired,
final RolloutGroupConditions conditions, final DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
@@ -228,8 +226,7 @@ private Rollout create0(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout create(
@NotNull @Valid Create create, int amountGroup, boolean confirmationRequired,
@NotNull RolloutGroupConditions conditions) {
@@ -238,8 +235,7 @@ public Rollout create(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout create(final Create rollout, final List groups, final RolloutGroupConditions conditions) {
if (groups.isEmpty()) {
throw new ValidationException("The amount of groups cannot be 0");
@@ -323,8 +319,7 @@ public boolean exists(final long rolloutId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void pauseRollout(final long rolloutId) {
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.RUNNING != rollout.getStatus()) {
@@ -340,8 +335,7 @@ public void pauseRollout(final long rolloutId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void resumeRollout(final long rolloutId) {
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.PAUSED != rollout.getStatus()) {
@@ -354,16 +348,14 @@ public void resumeRollout(final long rolloutId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision) {
return approveOrDeny0(rolloutId, decision, null);
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout approveOrDeny(final long rolloutId, final Rollout.ApprovalDecision decision, final String remark) {
return approveOrDeny0(rolloutId, decision, remark);
}
@@ -394,8 +386,7 @@ private Rollout approveOrDeny0(final long rolloutId, final Rollout.ApprovalDecis
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout start(final long rolloutId) {
log.debug("startRollout called for rollout {}", rolloutId);
@@ -408,8 +399,7 @@ public Rollout start(final long rolloutId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout update(final Update update) {
final JpaRollout rollout = rolloutRepository.getById(update.getId());
checkIfDeleted(update.getId(), rollout.getStatus());
@@ -420,8 +410,7 @@ public Rollout update(final Update update) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Rollout stop(long rolloutId) {
final JpaRollout jpaRollout = rolloutRepository.getById(rolloutId);
@@ -437,8 +426,7 @@ public Rollout stop(long rolloutId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void delete(final long rolloutId) {
this.delete0(rolloutRepository.getById(rolloutId));
}
@@ -466,8 +454,7 @@ public void cancelRolloutsForDistributionSet(final DistributionSet set, final Ac
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void triggerNextGroup(final long rolloutId) {
final JpaRollout rollout = rolloutRepository.getById(rolloutId);
if (RolloutStatus.RUNNING != rollout.getStatus()) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java
index 2b64bf8d4a..3925fefedb 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java
@@ -9,8 +9,6 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
-import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
import java.util.Collection;
@@ -32,6 +30,7 @@
import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -46,8 +45,7 @@
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@@ -137,7 +135,7 @@ public Map> findMetaDataBySoftwareModuleIdsAndTargetVi
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaSoftwareModule lock(final SoftwareModule softwareModule) {
final JpaSoftwareModule jpaSoftwareModule = toJpaSoftwareModule(softwareModule);
if (jpaSoftwareModule.isLocked()) {
@@ -153,7 +151,7 @@ public JpaSoftwareModule lock(final SoftwareModule softwareModule) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public JpaSoftwareModule unlock(final SoftwareModule softwareModule) {
final JpaSoftwareModule jpaSoftwareModule = toJpaSoftwareModule(softwareModule);
if (softwareModule.isLocked()) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSystemManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSystemManagement.java
index 45f8733751..460d090bd1 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSystemManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSystemManagement.java
@@ -49,15 +49,14 @@
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
-import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
+import org.springframework.boot.jpa.autoconfigure.JpaProperties;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
-import org.springframework.lang.Nullable;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.jspecify.annotations.Nullable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Propagation;
@@ -179,8 +178,7 @@ public TenantMetaData createTenantMetadata(final String tenant) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public TenantMetaData updateTenantMetadata(final long defaultDsType) {
final JpaTenantMetaData data = (JpaTenantMetaData) getTenantMetadataWithoutDetails();
data.setDefaultDsType(distributionSetTypeRepository.getById(defaultDsType));
@@ -188,8 +186,7 @@ public TenantMetaData updateTenantMetadata(final long defaultDsType) {
}
@Override
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void deleteTenant(final String t) {
if (artifactStorage == null) {
throw new IllegalStateException("Artifact repository is not available. Can't delete tenant.");
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java
index 623d190df0..da2611a8dd 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetManagement.java
@@ -61,8 +61,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -241,16 +240,14 @@ public long countByRsqlAndNonDsAndCompatibleAndUpdatable(final long distribution
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void deleteByControllerId(final String controllerId) {
jpaRepository.delete(jpaRepository.getByControllerId(controllerId));
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List assignTag(
final Collection controllerIds, final long targetTagId, final Consumer> notFoundHandler) {
return assignTag0(controllerIds, targetTagId, notFoundHandler);
@@ -258,8 +255,7 @@ public List assignTag(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List assignTag(final Collection controllerIds, final long targetTagId) {
return assignTag0(controllerIds, targetTagId, null);
}
@@ -284,8 +280,7 @@ public Set getTags(@NotEmpty String controllerId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List unassignTag(
final Collection controllerIds, final long targetTagId, final Consumer> notFoundHandler) {
return unassignTag0(controllerIds, targetTagId, notFoundHandler);
@@ -293,8 +288,7 @@ public List unassignTag(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public List unassignTag(final Collection controllerIds, final long targetTagId) {
return unassignTag0(controllerIds, targetTagId, null);
}
@@ -313,8 +307,7 @@ private List unassignTag0(
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Target assignType(final String controllerId, final Long targetTypeId) {
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
@@ -328,8 +321,7 @@ public Target assignType(final String controllerId, final Long targetTypeId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public Target unassignType(final String controllerId) {
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
target.setTargetType(null);
@@ -338,8 +330,7 @@ public Target unassignType(final String controllerId) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void assignTargetGroupWithRsql(String group, String rsql) {
final Specification rsqlSpecification = QLSupport.getInstance().buildSpec(rsql, TargetFields.class);
@@ -356,8 +347,7 @@ public void assignTargetGroupWithRsql(String group, String rsql) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void assignTargetsWithGroup(String group, List controllerIds) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaUpdate criteriaQuery = cb.createCriteriaUpdate(JpaTarget.class);
@@ -387,8 +377,7 @@ public List findGroups(String tenant) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void createMetadata(final String controllerId, final String key, final String value) {
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
@@ -404,8 +393,7 @@ public void createMetadata(final String controllerId, final String key, final St
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void createMetadata(final String controllerId, final Map md) {
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
@@ -434,8 +422,7 @@ public String getMetadata(final String controllerId, final String key) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void deleteMetadata(final String controllerId, final String key) {
final JpaTarget target = jpaRepository.getByControllerId(controllerId);
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTypeManagement.java
index 971cf15e7b..3dcd26fe48 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTypeManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetTypeManagement.java
@@ -36,8 +36,7 @@
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -94,10 +93,8 @@ public Optional findByKey(final String key) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
- public TargetType assignCompatibleDistributionSetTypes(final long id,
- final Collection distributionSetTypeIds) {
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
+ public TargetType assignCompatibleDistributionSetTypes(final long id, final Collection distributionSetTypeIds) {
final Collection dsTypes = distributionSetTypeRepository.findAllById(distributionSetTypeIds);
if (dsTypes.size() < distributionSetTypeIds.size()) {
@@ -115,8 +112,7 @@ public TargetType assignCompatibleDistributionSetTypes(final long id,
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public TargetType unassignDistributionSetType(final long id, final long distributionSetTypeId) {
final JpaTargetType type = jpaRepository.getById(id);
if (!distributionSetTypeRepository.existsById(distributionSetTypeId)) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTenantConfigurationManagement.java
index a0511d0f32..f6ebeacc08 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTenantConfigurationManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTenantConfigurationManagement.java
@@ -59,8 +59,7 @@
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.retry.annotation.Backoff;
-import org.springframework.retry.annotation.Retryable;
+import org.springframework.resilience.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@@ -100,16 +99,14 @@ public void onApplicationEvent(@NonNull final ContextRefreshedEvent event) {
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void addOrUpdateConfiguration(final String keyName, final Object value) {
addOrUpdateConfiguration0(Map.of(keyName, value));
}
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void addOrUpdateConfiguration(final Map configurations) {
addOrUpdateConfiguration0(configurations);
}
@@ -126,8 +123,7 @@ public TenantConfigurationValue getConfigurationValue(final String keyNam
@Override
@Transactional
- @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
- backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ @Retryable(includes = ConcurrencyFailureException.class, maxRetriesString = Constants.RETRY_MAX, delayString = Constants.RETRY_DELAY)
public void deleteConfiguration(final String keyName) {
tenantConfigurationRepository.deleteByKey(keyName);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
index 859b224d3f..72fb637c73 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java
@@ -23,7 +23,6 @@
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
-import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Convert;
import jakarta.persistence.Converter;
import jakarta.persistence.Entity;
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ACMRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ACMRepository.java
index 1b04f6b24d..6f788d3471 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ACMRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ACMRepository.java
@@ -13,11 +13,11 @@
import java.util.Optional;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.jspecify.annotations.NonNull;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Repository interface that offers some actions that takes in account a target operation.
@@ -30,46 +30,42 @@ public interface ACMRepository {
* Saves only if the caller have access for the operation over the entity. This method could be used to
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
*
- * @param operation access operationIf operation is null no access is checked! Should be used
- * only for tenant context.
+ * @param operation access operationIf operation is null no access is checked! Should be used only for tenant context.
* @param entity the entity to save
* @return the saved entity
*/
@NonNull
- S save(@Nullable AccessController.Operation operation, @NonNull final S entity);
+ S save(AccessController.Operation operation, @NonNull S entity);
/**
* Saves only if the caller have access for the operation over all entities. This method could be used to
* check CREATE access in creating an entity (save without operation would check for UPDATE access).
*
- * @param operation access operationIf operation is null no access is checked! Should be used
- * only for tenant context.
+ * @param operation access operationIf operation is null no access is checked! Should be used only for tenant context.
* @param entities the entities to save
* @return the saved entities
*/
- List saveAll(@Nullable AccessController.Operation operation, final Iterable entities);
+ List saveAll(AccessController.Operation operation, Iterable entities);
/**
* Returns single entry that match specification and the operation is allowed for.
*
- * @param operation access operation. If operation is null no access is checked! Should be used
- * only for tenant context.
+ * @param operation access operation. If operation is null no access is checked! Should be used only for tenant context.
* @param spec specification
* @return matching entity
*/
@NonNull
- Optional findOne(@Nullable AccessController.Operation operation, @NonNull Specification spec);
+ Optional findOne(AccessController.Operation operation, @NonNull Specification spec);
/**
* Returns all entries that match specification and the operation is allowed for.
*
- * @param operation access operation. If operation is null no access is checked! Should be used
- * only for tenant context.
+ * @param operation access operation. If operation is null no access is checked! Should be used only for tenant context.
* @param spec specification
* @return matching entities
*/
@NonNull
- List findAll(@Nullable AccessController.Operation operation, @Nullable Specification spec);
+ List findAll(AccessController.Operation operation, @Nullable Specification spec);
/**
* Returns all entries that match specification and the operation is allowed for.
@@ -79,8 +75,7 @@ public interface ACMRepository {
* @param spec specification
* @return matching entities
*/
- @NonNull
- boolean exists(@Nullable AccessController.Operation operation, Specification spec);
+ boolean exists(AccessController.Operation operation, Specification spec);
/**
* Returns count of all entries that match specification and the operation is allowed for.
@@ -90,8 +85,7 @@ public interface ACMRepository {
* @param spec specification
* @return count of matching entities
*/
- @NonNull
- long count(@Nullable AccessController.Operation operation, @Nullable Specification spec);
+ long count(AccessController.Operation operation, @Nullable Specification spec);
/**
* Returns all entries, without count, that match specification and the operation is allowed for.
@@ -103,9 +97,8 @@ public interface ACMRepository {
* @return count of matching entities
*/
@NonNull
- Slice findAllWithoutCount(
- @Nullable final AccessController.Operation operation, @Nullable Specification spec, Pageable pageable);
+ Slice findAllWithoutCount(AccessController.Operation operation, @Nullable Specification spec, Pageable pageable);
@NonNull
Class getDomainClass();
-}
+}
\ No newline at end of file
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ActionRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ActionRepository.java
index 9a7576a295..312a3cf555 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ActionRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/ActionRepository.java
@@ -12,7 +12,11 @@
import java.util.List;
import java.util.Optional;
+import jakarta.persistence.criteria.CriteriaBuilder;
+
+import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
+import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.model.Action;
@@ -74,14 +78,6 @@ void switchStatus(
@Param("statusToSet") Action.Status statusToSet, @Param("targetsIds") List targetIds,
@Param("active") boolean active, @Param("currentStatus") Action.Status currentStatus);
- /**
- * Retrieves an {@link Action} that matches the queried externalRef.
- *
- * @param externalRef of the action. See {@link Action#getExternalRef()}
- * @return the found {@link Action}
- */
- Optional findByExternalRef(@Param("externalRef") String externalRef);
-
/**
* Counts all {@link Action}s referring to the given target.
*
@@ -145,7 +141,19 @@ void switchStatus(
* @param statuses the list of statuses the action should not have
* @return the count of actions referring the rollout and rollout group and are not in given states
*/
- Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup, List statuses);
+ // TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
+// Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup, List statuses);
+ default Long countByRolloutAndRolloutGroupAndStatusNotIn(
+ final JpaRollout rollout, final JpaRolloutGroup rolloutGroup, final List statuses) {
+ return count((root, query, cb) -> {
+ final CriteriaBuilder.In in = cb.in(root.get(JpaAction_.status));
+ statuses.forEach(in::value);
+ return cb.and(
+ cb.equal(root.get(JpaAction_.rollout).get(AbstractJpaBaseEntity_.id), rollout.getId()),
+ cb.equal(root.get(JpaAction_.rolloutGroup).get(AbstractJpaBaseEntity_.id), rolloutGroup.getId()),
+ cb.not(in));
+ });
+ }
/**
* Counts all actions referring to a given rollout and rollout group.
@@ -160,7 +168,7 @@ void switchStatus(
/**
* Counts all actions referring to a given rollout, rollout group and status.
- *
+ *
* No access control applied
*
* @param rolloutId the ID of rollout the actions belong to
@@ -214,7 +222,7 @@ void switchStatus(
/**
* Returns {@code true} if actions for the given rollout exists, otherwise {@code false}
- *
+ *
* No access control applied
*
* @param rolloutId the ID of the rollout the actions belong to
@@ -222,13 +230,13 @@ void switchStatus(
* @return {@code true} if actions for the given rollout exists, otherwise {@code false}
*/
@Query("SELECT CASE WHEN COUNT(a)>0 THEN 'true' ELSE 'false' END FROM JpaAction a WHERE a.rollout.id=:rolloutId AND a.status != :status")
- boolean existsByRolloutIdAndStatusNotIn(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
+ boolean existsByRolloutIdAndStatusNot(@Param("rolloutId") Long rolloutId, @Param("status") Status status);
/**
* Retrieving all actions referring to a given rollout with a specific action as parent reference and a specific status.
- *
+ *
* Finding all actions of a specific rollout group parent relation.
- *
+ *
* No access control applied
*
* @param pageable page parameters
@@ -242,7 +250,7 @@ void switchStatus(
/**
* Retrieving all actions referring to the first group of a rollout.
- *
+ *
* No access control applied
*
* @param pageable page parameters
@@ -255,7 +263,7 @@ void switchStatus(
/**
* Retrieves all actions for a specific rollout and in a specific status.
- *
+ *
* No access control applied
*
* @param pageable page parameters
@@ -267,7 +275,7 @@ void switchStatus(
/**
* Get list of objects which has details of status and count of targets in each status in specified rollout.
- *
+ *
* No access control applied
*
* @param rolloutId id of {@link Rollout}
@@ -278,7 +286,7 @@ void switchStatus(
/**
* Get list of objects which has details of status and count of targets in each status in specified rollout.
- *
+ *
* No access control applied
*
* @param rolloutId id of {@link Rollout}
@@ -289,7 +297,7 @@ void switchStatus(
/**
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
- *
+ *
* No access control applied
*
* @param rolloutGroupId id of {@link RolloutGroup}
@@ -300,7 +308,7 @@ void switchStatus(
/**
* Get list of objects which has details of status and count of targets in each status in specified rollout group.
- *
+ *
* No access control applied
*
* @param rolloutGroupId list of id of {@link RolloutGroup}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepository.java
index 35cc956eaf..b947a4b412 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/BaseEntityRepository.java
@@ -27,7 +27,7 @@
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
import org.springframework.transaction.annotation.Transactional;
/**
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 d791f1b00f..abe94c9388 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
@@ -25,14 +25,17 @@
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.eclipse.hawkbit.repository.jpa.acm.AccessController.Operation;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.DeleteSpecification;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
+import org.springframework.data.jpa.domain.UpdateSpecification;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
@Slf4j
public class BaseEntityRepositoryACM implements BaseEntityRepository {
@@ -52,7 +55,7 @@ public class BaseEntityRepositoryACM implements
@Override
@NonNull
public S save(@NonNull final S entity) {
- accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entity);
+ accessController.assertOperationAllowed(Operation.UPDATE, entity);
return repository.save(entity);
}
@@ -75,15 +78,15 @@ public boolean existsById(@NonNull final Long id) {
@Override
public long count() {
- return count(null);
+ return count((Specification) null);
}
@Override
public void deleteById(@NonNull final Long id) {
- if (!exists(AccessController.Operation.READ, byIdSpec(id))) {
+ if (!exists(Operation.READ, byIdSpec(id))) {
throw new EntityNotFoundException(repository.getDomainClass(), id);
}
- if (!exists(AccessController.Operation.DELETE, byIdSpec(id))) {
+ if (!exists(Operation.DELETE, byIdSpec(id))) {
throw new InsufficientPermissionException();
}
repository.deleteById(id);
@@ -91,7 +94,7 @@ public void deleteById(@NonNull final Long id) {
@Override
public void delete(@NonNull final T entity) {
- accessController.assertOperationAllowed(AccessController.Operation.DELETE, entity);
+ accessController.assertOperationAllowed(Operation.DELETE, entity);
repository.delete(entity);
}
@@ -99,7 +102,7 @@ public void delete(@NonNull final T entity) {
public void deleteAllById(@NonNull final Iterable extends Long> ids) {
final Set idList = new HashSet<>();
ids.forEach(idList::add);
- if (count(AccessController.Operation.DELETE, byIdsSpec(idList)) != idList.size()) {
+ if (count(Operation.DELETE, byIdsSpec(idList)) != idList.size()) {
throw new InsufficientPermissionException("Has at least one id that is not allowed for deletion!");
}
repository.deleteAllById(idList);
@@ -107,7 +110,7 @@ public void deleteAllById(@NonNull final Iterable extends Long> ids) {
@Override
public void deleteAll(@NonNull final Iterable extends T> entities) {
- accessController.assertOperationAllowed(AccessController.Operation.DELETE, entities);
+ accessController.assertOperationAllowed(Operation.DELETE, entities);
repository.deleteAll(entities);
}
@@ -124,7 +127,7 @@ public void deleteAll() {
@Override
public List saveAll(final Iterable entities) {
- accessController.assertOperationAllowed(AccessController.Operation.UPDATE, entities);
+ accessController.assertOperationAllowed(Operation.UPDATE, entities);
return repository.saveAll(entities);
}
@@ -142,7 +145,7 @@ public List findAllById(@NonNull final Iterable ids) {
@Override
public void deleteByTenant(final String tenant) {
- if (accessController.getAccessRules(AccessController.Operation.DELETE).isPresent()) {
+ if (accessController.getAccessRules(Operation.DELETE).isPresent()) {
throw new InsufficientPermissionException("DELETE operation has restriction for given context! deleteAll can't be executed!");
}
repository.deleteByTenant(tenant);
@@ -160,47 +163,52 @@ public Optional findOne(final Specification spec) {
return repository.findOne(
// spec shall be non-null and the result of appending rules shall be non-null
Objects.requireNonNull(
- accessController.appendAccessRules(AccessController.Operation.READ, spec),
+ accessController.appendAccessRules(Operation.READ, spec),
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL));
}
@Override
@NonNull
public List findAll(final Specification spec) {
- return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec));
+ return repository.findAll(accessController.appendAccessRules(Operation.READ, spec));
}
@Override
@NonNull
public Page findAll(final Specification spec, @NonNull final Pageable pageable) {
- return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
+ return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), pageable);
}
@Override
public Page findAll(final Specification spec, final Specification countSpec, final Pageable pageable) {
- return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), countSpec, pageable);
+ return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), countSpec, pageable);
}
@Override
@NonNull
public List findAll(final Specification spec, @NonNull final Sort sort) {
- return repository.findAll(accessController.appendAccessRules(AccessController.Operation.READ, spec), sort);
+ return repository.findAll(accessController.appendAccessRules(Operation.READ, spec), sort);
}
@Override
public long count(final Specification spec) {
- return repository.count(accessController.appendAccessRules(AccessController.Operation.READ, spec));
+ return repository.count(accessController.appendAccessRules(Operation.READ, spec));
}
@Override
public boolean exists(@NonNull final Specification spec) {
return repository.exists(
- Objects.requireNonNull(accessController.appendAccessRules(AccessController.Operation.READ, spec)));
+ Objects.requireNonNull(accessController.appendAccessRules(Operation.READ, spec)));
}
@Override
- public long delete(final Specification spec) {
- return repository.delete(accessController.appendAccessRules(AccessController.Operation.DELETE, spec));
+ public long update(final UpdateSpecification spec) {
+ return repository.update(accessController.appendAccessRules(Operation.UPDATE, spec));
+ }
+
+ @Override
+ public long delete(final DeleteSpecification spec) {
+ return repository.delete(accessController.appendAccessRules(Operation.DELETE, spec));
}
@Override
@@ -209,7 +217,7 @@ public R findBy(final Specification spec, final Function s
return repository.findBy(
// spec shall be non-null and the result of appending rules shall be non-null
Objects.requireNonNull(
- accessController.appendAccessRules(AccessController.Operation.READ, spec),
+ accessController.appendAccessRules(Operation.READ, spec),
APPENDED_ACCESS_RULES_SPEC_OF_NON_NULL_SPEC_MUST_NOT_BE_NULL),
queryFunction);
}
@@ -234,13 +242,13 @@ public Slice findAllWithoutCount(final Pageable pageable) {
@Override
public Slice findAllWithoutCount(final Specification spec, final Pageable pageable) {
return repository.findAllWithoutCount(
- accessController.appendAccessRules(AccessController.Operation.READ, spec), pageable);
+ accessController.appendAccessRules(Operation.READ, spec), pageable);
}
@Override
@Transactional
@NonNull
- public S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
+ public S save(Operation operation, @NonNull final S entity) {
if (operation != null) {
accessController.assertOperationAllowed(operation, entity);
}
@@ -249,7 +257,7 @@ public S save(@Nullable AccessController.Operation operation, @Non
@Override
@Transactional
- public List saveAll(@Nullable AccessController.Operation operation, final Iterable entities) {
+ public List saveAll(final Operation operation, final Iterable entities) {
if (operation != null) {
accessController.assertOperationAllowed(operation, entities);
}
@@ -257,7 +265,7 @@ public List saveAll(@Nullable AccessController.Operation operat
}
@NonNull
- public Optional findOne(@Nullable AccessController.Operation operation, @NonNull Specification spec) {
+ public Optional findOne(final Operation operation, @NonNull Specification spec) {
Objects.requireNonNull(spec, SPEC_MUST_NOT_BE_NULL);
if (operation == null) {
return repository.findOne(spec);
@@ -272,7 +280,7 @@ public Optional findOne(@Nullable AccessController.Operation operation, @NonN
@Override
@NonNull
- public List findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification spec) {
+ public List findAll(final Operation operation, @Nullable final Specification spec) {
if (operation == null) {
return repository.findAll(spec);
} else {
@@ -282,7 +290,7 @@ public List findAll(@Nullable final AccessController.Operation operation, @Nu
@Override
@NonNull
- public boolean exists(@Nullable AccessController.Operation operation, Specification spec) {
+ public boolean exists(final Operation operation, Specification spec) {
if (operation == null) {
return repository.exists(spec);
} else {
@@ -292,8 +300,7 @@ public boolean exists(@Nullable AccessController.Operation operation, Specificat
}
@Override
- @NonNull
- public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification spec) {
+ public long count(final Operation operation, @Nullable final Specification spec) {
if (operation == null) {
return repository.count(spec);
} else {
@@ -303,8 +310,7 @@ public long count(@Nullable final AccessController.Operation operation, @Nullabl
@Override
@NonNull
- public Slice findAllWithoutCount(
- @Nullable final AccessController.Operation operation, @Nullable Specification spec, Pageable pageable) {
+ public Slice findAllWithoutCount(final Operation operation, @Nullable Specification spec, Pageable pageable) {
if (operation == null) {
return repository.findAllWithoutCount(spec, pageable);
} else {
@@ -321,25 +327,25 @@ public Class getDomainClass() {
@Override
public Optional findOne(final Specification spec, final String entityGraph) {
return repository.findOne(
- accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph);
+ accessController.appendAccessRules(Operation.READ, spec), entityGraph);
}
@Override
public List findAll(final Specification spec, final String entityGraph) {
return repository.findAll(
- accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph);
+ accessController.appendAccessRules(Operation.READ, spec), entityGraph);
}
@Override
public Page findAll(final Specification spec, final String entityGraph, final Pageable pageable) {
return repository.findAll(
- accessController.appendAccessRules(AccessController.Operation.READ, spec), entityGraph, pageable);
+ 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(AccessController.Operation.READ, spec), entityGraph, sort);
+ accessController.appendAccessRules(Operation.READ, spec), entityGraph, sort);
}
@SuppressWarnings("unchecked")
@@ -370,7 +376,7 @@ static > R of
if (Iterable.class.isAssignableFrom(method.getReturnType())) {
for (final Object e : (Iterable>) result) {
if (repository.getDomainClass().isAssignableFrom(e.getClass())) {
- accessController.assertOperationAllowed(AccessController.Operation.READ, (T) e);
+ accessController.assertOperationAllowed(Operation.READ, (T) e);
}
}
} else if (Optional.class.isAssignableFrom(method.getReturnType()) && ((Optional>) result)
@@ -379,11 +385,11 @@ static > R of
return ((Optional) result).filter(
t -> {
// if not accessible - throws exception (as for iterables or single entities)
- accessController.assertOperationAllowed(AccessController.Operation.READ, t);
+ accessController.assertOperationAllowed(Operation.READ, t);
return true;
});
} else if (repository.getDomainClass().isAssignableFrom(method.getReturnType())) {
- accessController.assertOperationAllowed(AccessController.Operation.READ, (T) result);
+ accessController.assertOperationAllowed(Operation.READ, (T) result);
}
return result;
} else if ("toString".equals(method.getName()) && method.getParameterCount() == 0) {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/HawkbitBaseRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/HawkbitBaseRepository.java
index b879cba7a3..cf2a13f797 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/HawkbitBaseRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/HawkbitBaseRepository.java
@@ -19,7 +19,9 @@
import jakarta.persistence.TypedQuery;
import jakarta.transaction.Transactional;
-import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
+import org.eclipse.hawkbit.repository.jpa.acm.AccessController.Operation;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
@@ -30,8 +32,6 @@
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
-import org.springframework.lang.NonNull;
-import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
@@ -61,7 +61,7 @@ public HawkbitBaseRepository(final JpaEntityInformation entityInformation,
@Override
public Slice findAllWithoutCount(final Pageable pageable) {
- return findAllWithoutCount(null, pageable);
+ return findAllWithoutCount(Specification.unrestricted(), pageable);
}
@Override
@@ -74,38 +74,36 @@ public Slice findAllWithoutCount(@Nullable final Specification spec, final
@Transactional
@NonNull
@SuppressWarnings("java:S6809") // this method already has a transactional annotation witch shall be applied
- public S save(@Nullable AccessController.Operation operation, @NonNull final S entity) {
+ public S save(final Operation operation, @NonNull final S entity) {
return save(entity);
}
@Override
@Transactional
@SuppressWarnings("java:S6809") // this method already has a transactional annotation witch shall be applied
- public List saveAll(@Nullable AccessController.Operation operation, final Iterable entities) {
+ public List saveAll(final Operation operation, final Iterable entities) {
return saveAll(entities);
}
@NonNull
- public Optional findOne(@Nullable AccessController.Operation operation, @NonNull Specification spec) {
+ public Optional findOne(final Operation operation, @NonNull Specification spec) {
return findOne(spec);
}
@Override
@NonNull
@SuppressWarnings("java:S4449") // find all accepts null
- public List findAll(@Nullable final AccessController.Operation operation, @Nullable final Specification spec) {
+ public List findAll(@Nullable final Operation operation, @Nullable final Specification spec) {
return findAll(spec);
}
@Override
- @NonNull
- public boolean exists(@Nullable AccessController.Operation operation, Specification spec) {
+ public boolean exists(@Nullable Operation operation, Specification spec) {
return exists(spec);
}
@Override
- @NonNull
- public long count(@Nullable final AccessController.Operation operation, @Nullable final Specification spec) {
+ public long count(@Nullable final Operation operation, @Nullable final Specification spec) {
return count(spec);
}
@@ -137,7 +135,7 @@ public List findAll(final Specification spec, final String entityGraph, fi
@NonNull
@Override
- public Slice findAllWithoutCount(@Nullable final AccessController.Operation operation, @Nullable Specification spec,
+ public Slice findAllWithoutCount(@Nullable final Operation operation, @Nullable Specification spec,
Pageable pageable) {
return findAllWithoutCount(spec, pageable);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/JpaSpecificationEntityGraphExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/JpaSpecificationEntityGraphExecutor.java
index 0350202b1d..02eac98fd6 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/JpaSpecificationEntityGraphExecutor.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/JpaSpecificationEntityGraphExecutor.java
@@ -16,7 +16,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Repository interface that offers JpaSpecificationExecutor#findOne/All methods with entity graph loading
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/NoCountSliceRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/NoCountSliceRepository.java
index 8a0a64c3ac..eb78ee0044 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/NoCountSliceRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/NoCountSliceRepository.java
@@ -13,7 +13,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
/**
* Repository interface that offers findAll with disabled count query.
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java
index 36a375fe4e..5a50b84e72 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutGroupRepository.java
@@ -12,8 +12,12 @@
import java.util.Collection;
import java.util.List;
+import jakarta.persistence.criteria.CriteriaBuilder;
+
+import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
+import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
@@ -72,10 +76,20 @@ public interface RolloutGroupRepository extends BaseEntityRepository findByRolloutAndStatusNotIn(JpaRollout rollout, Collection status);
+ // TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
+// List findByRolloutAndStatusNotIn(JpaRollout rollout, Collection statuses);
+ default List findByRolloutAndStatusNotIn(final JpaRollout rollout, final Collection statuses) {
+ return findAll((root, query, cb) -> {
+ final CriteriaBuilder.In in = cb.in(root.get(JpaRolloutGroup_.status));
+ statuses.forEach(in::value);
+ return cb.and(
+ cb.equal(root.get(JpaRolloutGroup_.rollout).get(AbstractJpaBaseEntity_.id), rollout.getId()),
+ cb.not(in));
+ });
+ }
/**
* Retrieves all {@link RolloutGroup} for a specific rollout.
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutRepository.java
index 27d0f904b9..0e838e0ac0 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutRepository.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/repository/RolloutRepository.java
@@ -14,12 +14,18 @@
import java.util.Optional;
import jakarta.persistence.EntityManager;
+import jakarta.persistence.criteria.CriteriaBuilder;
+import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
+import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_;
+import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
+import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
+import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@@ -37,6 +43,9 @@ public interface RolloutRepository extends BaseEntityRepository {
* @param status the status of the rollouts to find
* @return the list of {@link Rollout} for specific status
*/
+ // TODO (Spring Boot 4 Migration): seems native queries are fine with collections. However, this is used only
+ // in handleAll and maybe the caller method RolloutManagement#findActiveRollouts could be moved in handler
+ // less visibility
@Query("SELECT sm.id FROM JpaRollout sm WHERE sm.status IN ?1 ORDER BY sm.id ASC")
List findByStatusIn(Collection status);
@@ -65,18 +74,36 @@ public interface RolloutRepository extends BaseEntityRepository {
* Retrieves all {@link Rollout}s for a specific {@link DistributionSet} in a given {@link RolloutStatus}.
*
* @param set the distribution set
- * @param status the status of the rollout
+ * @param statuses the status of the rollout
* @return {@link Rollout} for specific distribution set
*/
- List findByDistributionSetAndStatusIn(DistributionSet set, Collection status);
+ // TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
+// List findByDistributionSetAndStatusIn(DistributionSet set, Collection statusese);
+ default List findByDistributionSetAndStatusIn(final DistributionSet set, final Collection statuses) {
+ return findAll(byDsAndStatuses(set.getId(), statuses));
+ }
/**
* Counts all {@link Rollout}s for a specific {@link DistributionSet} in a
* given {@link RolloutStatus}.
*
* @param distributionSetId the distribution set
- * @param status the status of the rollout
+ * @param statuses the status of the rollout
* @return the count
*/
- long countByDistributionSetIdAndStatusIn(long distributionSetId, Collection status);
+ // TODO (Spring Boot 4 Migration): Eclipse link (5 beta) doesn't handle correctly the IN clause generated by Spring Data JPA
+// long countByDistributionSetIdAndStatusIn(long distributionSetId, Collection statuses);
+ default long countByDistributionSetIdAndStatusIn(final long distributionSetId, final Collection statuses) {
+ return count(byDsAndStatuses(distributionSetId, statuses));
+ }
+
+ private static Specification