[#2845] Bump Spring boot to 4.x (#2941)

Notes:
1. (!) Eclipselink shall be migrated to 5.0 (in 4.0.8 there are incompatible classes, e.g EJBQueryImpl doesn't implement some newer methods). In the moment is with beta (5.0.0-B12) - JUST for testing!
2. (!) Ethlo plugin doesn't work with Eclipselink 5.0, it builds with Eclipselink 4.0.8 (could be a problem)
3. Dependencies - new starters, test starters changes, some dependencies refactoring
4. Auto-configs split - package changes, some properties classes changes
5. Spring nullable org.springframework.lang.Nullable/NonNull are depecated and replaced with jspcify -> org.jspecify.annotations.Nullable/NonNull (NullMarked)
6. Lombok config - adding lombok.addNullAnnotations=jspecify - to do not mess annotations
7. Distributed lock table changes - SP_LOCK table db migration
8. Spring Retry replaced with Spring Core Retry - does repace retry in hawkbit
9. Specifications -> added Update/Delete(/Predicate) Specifications and JpaSpecificationExecutor changed
10. HawkbitBaseRepositoryFactoryBean modified to register properly
11. Jackson - 2 -> 3, package migrations, finals are not deserialized by default(enable finals deserialization, consider make non-final), too ‘smart’ tries to set complex objects instead of using non args constructor (-> @JsonIgnore), some other default configs made

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2026-04-14 11:31:41 +03:00
committed by GitHub
parent 23cd368e00
commit 1be473b22c
172 changed files with 1254 additions and 1045 deletions

View File

@@ -27,26 +27,5 @@
<artifactId>hawkbit-core</artifactId> <artifactId>hawkbit-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<!-- TEST -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -27,18 +27,5 @@
<artifactId>hawkbit-artifact-api</artifactId> <artifactId>hawkbit-artifact-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -19,7 +19,6 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage; import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
import org.eclipse.hawkbit.artifact.ArtifactStorage; import org.eclipse.hawkbit.artifact.ArtifactStorage;
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException; import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
@@ -47,7 +46,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
@Override @Override
public void deleteBySha1(final String tenant, final String sha1) { public void deleteBySha1(final String tenant, final String sha1) {
FileUtils.deleteQuietly(getFile(tenant, sha1)); deleteSilent(getFile(tenant, sha1));
} }
@Override @Override
@@ -65,7 +64,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
@Override @Override
public void deleteByTenant(final String tenant) { public void deleteByTenant(final String tenant) {
FileUtils.deleteQuietly(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile()); deleteSilent(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
} }
@Override @Override
@@ -78,7 +77,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
throws IOException { throws IOException {
final File fileSHA1Naming = getFile(tenant, base16Hashes.sha1()); final File fileSHA1Naming = getFile(tenant, base16Hashes.sha1());
if (fileSHA1Naming.exists()) { if (fileSHA1Naming.exists()) {
FileUtils.deleteQuietly(tempFile); deleteSilent(tempFile);
} else { } else {
Files.move(tempFile.toPath(), fileSHA1Naming.toPath()); Files.move(tempFile.toPath(), fileSHA1Naming.toPath());
} }
@@ -107,4 +106,24 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
final String folder2 = sha1.substring(length - 2, length); final String folder2 = sha1.substring(length - 2, length);
return Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant), folder1, folder2); 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
}
}
}
} }

View File

@@ -15,11 +15,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Random; import java.util.Random;
import lombok.extern.slf4j.Slf4j; 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.assertj.core.api.Assertions;
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage; import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException; import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
@@ -54,7 +54,7 @@ class FileArtifactStorageTest {
static void afterClass() { static void afterClass() {
if (new File(artifactResourceProperties.getPath()).exists()) { if (new File(artifactResourceProperties.getPath()).exists()) {
try { try {
FileUtils.deleteDirectory(new File(artifactResourceProperties.getPath())); delete(new File(artifactResourceProperties.getPath()));
} catch (final IOException | IllegalArgumentException e) { } catch (final IOException | IllegalArgumentException e) {
log.warn("Cannot delete file-directory", e); log.warn("Cannot delete file-directory", e);
} }
@@ -69,9 +69,10 @@ class FileArtifactStorageTest {
final byte[] fileContent = randomBytes(); final byte[] fileContent = randomBytes();
final StoredArtifactInfo artifact = storeRandomArtifact(fileContent); final StoredArtifactInfo artifact = storeRandomArtifact(fileContent);
final byte[] readContent = new byte[fileContent.length]; try (final InputStream is = artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1())) {
IOUtils.read(artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1()), readContent); final byte[] readContent = is.readAllBytes();
assertThat(readContent).isEqualTo(fileContent); assertThat(readContent).isEqualTo(fileContent);
}
} }
/** /**
@@ -141,4 +142,20 @@ class FileArtifactStorageTest {
return artifactFilesystemRepository.store(TENANT, inputStream, "filename.tmp", "application/txt", null); 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());
}
}
} }

View File

@@ -44,13 +44,6 @@
<artifactId>protostuff-core</artifactId> <artifactId>protostuff-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>io.protostuff</groupId> <groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId> <artifactId>protostuff-runtime</artifactId>

View File

@@ -18,8 +18,8 @@ import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties; import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.security.autoconfigure.SecurityProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;

View File

@@ -11,8 +11,8 @@ package org.eclipse.hawkbit.autoconfigure.security;
import org.eclipse.hawkbit.auth.StaticAuthenticationProvider; import org.eclipse.hawkbit.auth.StaticAuthenticationProvider;
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties; import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.security.autoconfigure.SecurityProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;

View File

@@ -24,72 +24,43 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId> <artifactId>spring-boot-starter-security-oauth2-client</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-boot-starter-cache</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-data-commons</artifactId> <artifactId>spring-boot-starter-webmvc</artifactId>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-context-support</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-security-oauth2-core</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<!-- needed for web MDC filter -->
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency> <dependency>
<groupId>jakarta.validation</groupId> <groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId> <artifactId>jakarta.validation-api</artifactId>
</dependency> </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.github.ben-manes.caffeine</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- needed for JSON context serialization -->
<optional>true</optional>
</dependency>
<!-- TEST -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -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:
* <ul>
* <li>Import {@link JacksonAutoConfiguration} and applies hawkbit-jackson-defaults.properties in order to configure the {@link tools.jackson.databind.json.JsonMapper}.</li>
* </ul>
*/
@Configuration
@Import(JacksonAutoConfiguration.class)
@PropertySource("classpath:/hawkbit-jackson-defaults.properties")
public class HawkbitAutoConfiguration {}

View File

@@ -20,7 +20,7 @@ import java.util.regex.Pattern;
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails; import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.tenancy.TenantAwareUser; import org.eclipse.hawkbit.tenancy.TenantAwareUser;
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties; 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.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;

View File

@@ -20,8 +20,6 @@ import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -36,6 +34,7 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import tools.jackson.databind.ObjectMapper;
/** /**
* A 'static' class providing methods related to access context: * A 'static' class providing methods related to access context:
@@ -298,11 +297,7 @@ public class AccessContext {
@SuppressWarnings("java:S112") // java:S112 - generic method @SuppressWarnings("java:S112") // java:S112 - generic method
private static String serialize(final SecurityContext securityContext) { private static String serialize(final SecurityContext securityContext) {
Objects.requireNonNull(securityContext); Objects.requireNonNull(securityContext);
try { return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
} catch (final JsonProcessingException e) {
throw new RuntimeException(e);
}
} }
/** /**
@@ -314,12 +309,7 @@ public class AccessContext {
@SuppressWarnings("java:S112") // java:S112 - generic method @SuppressWarnings("java:S112") // java:S112 - generic method
private static SecurityContext deserialize(final String securityContextString) { private static SecurityContext deserialize(final String securityContextString) {
Objects.requireNonNull(securityContextString); Objects.requireNonNull(securityContextString);
final String securityContextTrimmed = securityContextString.trim(); return OBJECT_MAPPER.readerFor(SecCtxInfo.class).<SecCtxInfo> readValue(securityContextString.trim()).toSecurityContext();
try {
return OBJECT_MAPPER.readerFor(SecCtxInfo.class).<SecCtxInfo> readValue(securityContextTrimmed).toSecurityContext();
} catch (final JsonProcessingException e) {
throw new RuntimeException(e);
}
} }
private static boolean isAuthenticationInvalid(final Authentication authentication) { private static boolean isAuthenticationInvalid(final Authentication authentication) {

View File

@@ -14,31 +14,29 @@ import java.util.NoSuchElementException;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import jakarta.servlet.DispatcherType;
import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues; import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tag;
import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.ObservationRegistry;
import lombok.NonNull; import lombok.NonNull;
import org.eclipse.hawkbit.context.AccessContext; 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.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 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.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener; import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention; import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext; import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;
import org.springframework.web.filter.ServerHttpObservationFilter; import org.springframework.web.filter.ServerHttpObservationFilter;
@AutoConfiguration @AutoConfiguration
@@ -57,11 +55,11 @@ public class DefaultTenantConfiguration {
} }
@AutoConfiguration(afterName = { @AutoConfiguration(afterName = {
"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration", "org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration",
"org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration" }) "org.springframework.boot.security.autoconfigure.SecurityAutoConfiguration" })
@ConditionalOnProperty(name = "hawkbit.metrics.tenancy.web.enabled", havingValue = "true", matchIfMissing = true) @ConditionalOnProperty(name = "hawkbit.metrics.tenancy.web.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @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) @ConditionalOnBean(ObservationRegistry.class)
public static class WebConfig { public static class WebConfig {
@@ -87,31 +85,40 @@ public class DefaultTenantConfiguration {
@Primary @Primary
public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter( public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(
final ObservationRegistry registry, final ObservationRegistry registry,
// should be serverRequestObservationConvention (registered above) final SecurityFilterProperties securityFilterProperties) {
final ObjectProvider<ServerRequestObservationConvention> customConvention, final FilterRegistrationBean<ServerHttpObservationFilter> filterRegistrationBean = new FilterRegistrationBean<>(
final ObservationProperties observationProperties, new ServerHttpObservationFilter(registry, new DefaultServerRequestObservationConvention() {
final SecurityProperties securityProperties) {
final FilterRegistrationBean<ServerHttpObservationFilter> filterRegistrationBean = new WebMvcObservationAutoConfiguration() @NonNull
.webMvcObservationFilter(registry, customConvention, observationProperties); @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 // after security filter, so to be able to log tenant
filterRegistrationBean.setOrder(securityProperties.getFilter().getOrder() + 1); filterRegistrationBean.setOrder(securityFilterProperties.getOrder() + 1);
return filterRegistrationBean; 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) @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 { public static class RepositoryConfig {
@Bean @Bean
public RepositoryTagsProvider repositoryTagsProvider() { public RepositoryTagsProvider repositoryTagsProvider() {
return new DefaultRepositoryTagsProvider() { return new DefaultRepositoryTagsProvider() {
@NonNull
@Override @Override
public Iterable<Tag> repositoryTags(final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) { public Iterable<Tag> repositoryTags(@NonNull final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) {
final Iterable<Tag> defaultTags = super.repositoryTags(invocation); final Iterable<Tag> defaultTags = super.repositoryTags(invocation);
final String tenant = TENANT_TAG_VALUE_PROVIDER.get(); final String tenant = TENANT_TAG_VALUE_PROVIDER.get();
return () -> { return () -> {

View File

@@ -31,7 +31,7 @@ import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.AbstractValueAdaptingCache; import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment; 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. * A spring Cache Manager that handles the multi tenancy.

View File

@@ -1,3 +1,4 @@
org.eclipse.hawkbit.HawkbitAutoConfiguration
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.WebConfig org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.WebConfig
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.RepositoryConfig org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.RepositoryConfig

View File

@@ -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

View File

@@ -14,7 +14,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.springframework.lang.Nullable; import org.jspecify.annotations.Nullable;
@Data @Data
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)

View File

@@ -40,7 +40,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId> <groupId>tools.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-cbor</artifactId> <artifactId>jackson-dataformat-cbor</artifactId>
</dependency> </dependency>
@@ -64,5 +64,11 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -18,20 +18,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; 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.context.AccessContext;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback; import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion; import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
@@ -54,6 +45,11 @@ import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 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( @ContextConfiguration(
classes = { DdiApiConfiguration.class, RestConfiguration.class, JpaRepositoryConfiguration.class, TestConfiguration.class }) 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 * @param json JSON object to convert
* @return Equivalent CBOR data * @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 JsonFactory jsonFactory = new JsonFactory();
final JsonParser jsonParser = jsonFactory.createParser(json); final JsonParser jsonParser = jsonFactory.createParser(json);
final ByteArrayOutputStream out = new ByteArrayOutputStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream();
final CBORFactory cborFactory = new CBORFactory(); final CBORFactory cborFactory = new CBORFactory();
final CBORGenerator cborGenerator = cborFactory.createGenerator(out); final JsonGenerator cborGenerator = cborFactory.createGenerator(out);
while (jsonParser.nextToken() != null) { while (jsonParser.nextToken() != null) {
cborGenerator.copyCurrentEvent(jsonParser); cborGenerator.copyCurrentEvent(jsonParser);
} }
@@ -106,11 +101,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
* *
* @param input CBOR data to convert * @param input CBOR data to convert
* @return Equivalent JSON string * @return Equivalent JSON string
* @throws IOException Invalid CBOR input
*/ */
protected static String cborToJson(final byte[] input) throws IOException { protected static String cborToJson(final byte[] input) {
final CBORFactory cborFactory = new CBORFactory(); final JsonParser cborParser = new CBORFactory().createParser(input);
final CBORParser cborParser = cborFactory.createParser(input);
final JsonFactory jsonFactory = new JsonFactory(); final JsonFactory jsonFactory = new JsonFactory();
final StringWriter stringWriter = new StringWriter(); final StringWriter stringWriter = new StringWriter();
final JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter); final JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
@@ -210,107 +203,92 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
controllerId + "/deploymentBase/" + actionId; controllerId + "/deploymentBase/" + actionId;
} }
protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException { protected String getJsonRejectedCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
} }
protected String getJsonRejectedDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonRejectedDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
} }
protected String getJsonDownloadDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonDownloadDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
} }
protected String getJsonDownloadedDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonDownloadedDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
} }
protected String getJsonCanceledCancelActionFeedback() throws JsonProcessingException { protected String getJsonCanceledCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
} }
protected String getJsonCanceledDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonCanceledDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
} }
protected String getJsonScheduledCancelActionFeedback() throws JsonProcessingException { protected String getJsonScheduledCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS,
DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("scheduled")); Collections.singletonList("scheduled"));
} }
protected String getJsonScheduledDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonScheduledDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
} }
protected String getJsonResumedCancelActionFeedback() throws JsonProcessingException { protected String getJsonResumedCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
} }
protected String getJsonResumedDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonResumedDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
} }
protected String getJsonProceedingCancelActionFeedback() throws JsonProcessingException { protected String getJsonProceedingCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(
DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.SUCCESS, Collections.singletonList("proceeding")); DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.SUCCESS, Collections.singletonList("proceeding"));
} }
protected String getJsonProceedingDeploymentActionFeedback() throws JsonProcessingException { protected String getJsonProceedingDeploymentActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
} }
protected String getJsonClosedCancelActionFeedback() throws JsonProcessingException { protected String getJsonClosedCancelActionFeedback() {
return getJsonActionFeedback( return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("closed"));
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")); return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE, Collections.singletonList("closed"));
} }
protected String getJsonActionFeedback( protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) {
final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) throws JsonProcessingException {
return getJsonActionFeedback(executionStatus, finalResult, Collections.singletonList(randomString(1000))); return getJsonActionFeedback(executionStatus, finalResult, Collections.singletonList(randomString(1000)));
} }
protected String getJsonActionFeedback( protected String getJsonActionFeedback(
final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult, final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult, final List<String> messages) {
final List<String> messages) throws JsonProcessingException {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages); final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages);
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus)); return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
} }
protected String getJsonActionFeedback( protected String getJsonActionFeedback(
final DdiStatus.ExecutionStatus executionStatus, final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult, final List<String> messages) {
final DdiResult.FinalResult finalResult, final List<String> messages) throws JsonProcessingException {
return getJsonActionFeedback(executionStatus, finalResult, null, messages); return getJsonActionFeedback(executionStatus, finalResult, null, messages);
} }
protected String getJsonActionFeedback( protected String getJsonActionFeedback(
final DdiStatus.ExecutionStatus executionStatus, final DdiStatus.ExecutionStatus executionStatus,
final DdiResult.FinalResult finalResult, final Integer code, final List<String> messages) throws JsonProcessingException { final DdiResult.FinalResult finalResult, final Integer code, final List<String> messages) {
final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)), code, messages); final DdiStatus ddiStatus = new DdiStatus(executionStatus, new DdiResult(finalResult, new DdiProgress(2, 5)), code, messages);
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus)); return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
} }
protected String getJsonConfirmationFeedback( protected String getJsonConfirmationFeedback(
final DdiConfirmationFeedback.Confirmation confirmation, final DdiConfirmationFeedback.Confirmation confirmation, final Integer code, final List<String> messages) {
final Integer code, final List<String> messages) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(new DdiConfirmationFeedback(confirmation, code, 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)); return OBJECT_MAPPER.writeValueAsString(new DdiAssignedVersion(name, version));
} }

View File

@@ -32,6 +32,11 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Database --> <!-- Database -->
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>

View File

@@ -53,7 +53,7 @@
<!-- Spring - START --> <!-- Spring - START -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-webmvc</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@@ -76,7 +76,7 @@ class ControllerDownloadSecurityConfiguration {
@Bean @Bean
@Order(300) // higher priority than HawkBit DDI security, so that the DDI DL security is applied first @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 http
.securityMatcher(DDI_DL_ANT_MATCHER) .securityMatcher(DDI_DL_ANT_MATCHER)
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated()) .authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())

View File

@@ -40,11 +40,7 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId> <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>

View File

@@ -184,7 +184,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) { protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
final Message message = MessageBuilder final Message message = MessageBuilder
.withBody(String.valueOf(java.lang.System.currentTimeMillis()).getBytes()) .withBody(String.valueOf(System.currentTimeMillis()).getBytes())
.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN) .setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
.setCorrelationId(ping.getMessageProperties().getCorrelationId()) .setCorrelationId(ping.getMessageProperties().getCorrelationId())
.setHeader(MessageHeaderKey.TYPE, MessageType.PING_RESPONSE) .setHeader(MessageHeaderKey.TYPE, MessageType.PING_RESPONSE)
@@ -457,7 +457,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
// due to the fact that all targets in a batch use the same set of software modules we don't generate target-specific urls // 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 Target firstTarget = targets.get(0);
final DmfBatchDownloadAndUpdateRequest batchRequest = new DmfBatchDownloadAndUpdateRequest( final DmfBatchDownloadAndUpdateRequest batchRequest = new DmfBatchDownloadAndUpdateRequest(
java.lang.System.currentTimeMillis(), System.currentTimeMillis(),
dmfTargets, dmfTargets,
Optional.ofNullable(modules) Optional.ofNullable(modules)
.map(Map::entrySet) .map(Map::entrySet)

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.sql.SQLException; import java.sql.SQLException;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -32,9 +33,9 @@ import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler
import org.springframework.amqp.rabbit.listener.FatalExceptionStrategy; import org.springframework.amqp.rabbit.listener.FatalExceptionStrategy;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory; import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; 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.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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -42,9 +43,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.core.retry.RetryPolicy;
import org.springframework.retry.support.RetryTemplate; import org.springframework.core.retry.RetryTemplate;
import org.springframework.util.ErrorHandler; import org.springframework.util.ErrorHandler;
import tools.jackson.databind.json.JsonMapper;
/** /**
* Spring configuration for AMQP based DMF communication for indirect device integration. * Spring configuration for AMQP based DMF communication for indirect device integration.
@@ -91,13 +93,16 @@ public class DmfApiConfiguration {
* @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}. * @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}.
*/ */
@Bean @Bean
public RabbitTemplate rabbitTemplate() { public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory); final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter(jsonMapper));
final RetryTemplate retryTemplate = new RetryTemplate(); // the same policy the previously used default ExponentialBackOffPolicy applied
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy()); rabbitTemplate.setRetryTemplate(new RetryTemplate(RetryPolicy.builder()
rabbitTemplate.setRetryTemplate(retryTemplate); .delay(Duration.ofMillis(100))
.multiplier(2)
.maxDelay(Duration.ofSeconds(30))
.build()));
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) { if (ack) {
@@ -136,8 +141,8 @@ public class DmfApiConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public AmqpMessageSenderService amqpSenderServiceBean() { public AmqpMessageSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
return new DefaultAmqpMessageSenderService(rabbitTemplate()); return new DefaultAmqpMessageSenderService(rabbitTemplate);
} }
/** /**
@@ -203,5 +208,4 @@ public class DmfApiConfiguration {
return false; return false;
} }
} }
} }

View File

@@ -54,7 +54,7 @@ import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConversionException; import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
@@ -108,7 +108,7 @@ class AmqpMessageHandlerServiceTest {
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
void before() { void before() {
TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagement); TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagement);
messageConverter = new Jackson2JsonMessageConverter(); messageConverter = new JacksonJsonMessageConverter();
lenient().when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); lenient().when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
amqpMessageHandlerService = new AmqpMessageHandlerService( amqpMessageHandlerService = new AmqpMessageHandlerService(

View File

@@ -751,7 +751,6 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr
// retrieve action and ensure that it is in Running state // retrieve action and ensure that it is in Running state
final Action action = deploymentManagement.findAction(actionId).orElseThrow(() -> new AssertionError("Action not found!")); final Action action = deploymentManagement.findAction(actionId).orElseThrow(() -> new AssertionError("Action not found!"));
assertThat(action.getStatus()).isEqualTo(Status.RUNNING); assertThat(action.getStatus()).isEqualTo(Status.RUNNING);
} }
/** /**

View File

@@ -0,0 +1,2 @@
# Disable nullability annotations in order to do not bring jspecify as a dependency
lombok.addNullAnnotations=none

View File

@@ -52,18 +52,11 @@
<dependency> <dependency>
<groupId>com.rabbitmq</groupId> <groupId>com.rabbitmq</groupId>
<artifactId>http-client</artifactId> <artifactId>http-client</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.hawkbit</groupId> <groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId> <artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.awaitility</groupId> <groupId>org.awaitility</groupId>

View File

@@ -45,11 +45,6 @@
<groupId>org.mariadb.jdbc</groupId> <groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId> <artifactId>mariadb-java-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.mcp.server;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.security.autoconfigure.UserDetailsServiceAutoConfiguration;
/** /**
* Standalone MCP Server application that connects to hawkBit via REST API. * Standalone MCP Server application that connects to hawkBit via REST API.

View File

@@ -31,5 +31,11 @@
<artifactId>hawkbit-rest-api</artifactId> <artifactId>hawkbit-rest-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId; import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
@@ -17,6 +18,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
/** /**
* Request Body of DistributionSet for assignment operations (ID only). * Request Body of DistributionSet for assignment operations (ID only).
*/ */
@NoArgsConstructor
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)

View File

@@ -41,13 +41,11 @@ class MgmtTargetAssignmentResponseBodyTest {
assertThat(jsonNode.has("assigned")).as("the assigned targets count").isTrue(); 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").isNumber()).as("the assigned targets count").isTrue();
assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count") assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count").isEqualTo(ASSIGNED_ACTIONS.size());
.isEqualTo(ASSIGNED_ACTIONS.size());
assertThat(jsonNode.has("alreadyAssigned")).as("the already assigned targets count").isTrue(); 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").isNumber()).as("the already assigned targets count").isTrue();
assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count") assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count").isEqualTo(ALREADY_ASSIGNED_COUNT);
.isEqualTo(ALREADY_ASSIGNED_COUNT);
assertThat(jsonNode.has("total")).as("the total targets count").isTrue(); assertThat(jsonNode.has("total")).as("the total targets count").isTrue();
assertThat(jsonNode.get("total").isNumber()).as("the total targets count").isTrue(); assertThat(jsonNode.get("total").isNumber()).as("the total targets count").isTrue();

View File

@@ -59,5 +59,11 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -17,6 +17,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.stereotype.Controller; 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 * 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 @@ import org.springframework.stereotype.Controller;
@ComponentScan @ComponentScan
@Import({ RestConfiguration.class, OpenApi.class }) @Import({ RestConfiguration.class, OpenApi.class })
@PropertySource("classpath:/hawkbit-mgmt-api-defaults.properties") @PropertySource("classpath:/hawkbit-mgmt-api-defaults.properties")
public class MgmtApiConfiguration {} public class MgmtApiConfiguration implements WebMvcConfigurer {}

View File

@@ -32,8 +32,8 @@ import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; 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.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;

View File

@@ -92,7 +92,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired; 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.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
@@ -145,8 +145,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
ActionRepository actionRepository; ActionRepository actionRepository;
@Autowired @Autowired
private JpaProperties jpaProperties; private JpaProperties jpaProperties;
@Autowired
private ObjectMapper objectMapper;
/** /**
* Ensures that when targetType value of -1 is provided the target type is unassigned from the target. * Ensures that when targetType value of -1 is provided the target type is unassigned from the target.
@@ -254,7 +252,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value", "custom_remark_value"); final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value", "custom_remark_value");
mvc.perform(post(TARGETS_V1 + "/{targetId}/" + AUTO_CONFIRM + "/" + ACTIVATE, mvc.perform(post(TARGETS_V1 + "/{targetId}/" + AUTO_CONFIRM + "/" + ACTIVATE,
testTarget.getControllerId()) testTarget.getControllerId())
.content(objectMapper.writeValueAsString(body)).contentType(APPLICATION_JSON)) .content(OBJECT_MAPPER.writeValueAsString(body)).contentType(APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andExpect(status().isNoContent()); .andExpect(status().isNoContent());
} }

View File

@@ -571,13 +571,13 @@ class MgmtTargetTypeResourceTest extends AbstractManagementApiIntegrationTest {
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("{\"id\":1}") // actually it is semi invalid could become bad request - not strict but yet accepted
.contentType(MediaType.APPLICATION_JSON)) // 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()) .andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest()); .andExpect(status().isNotFound());
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("[{\"id\":44456}]") mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).contentType(MediaType.APPLICATION_JSON).content("[{\"id\":44456}]"))
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()) .andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound()); .andExpect(status().isNotFound());

View File

@@ -32,6 +32,11 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Database --> <!-- Database -->
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>

View File

@@ -14,9 +14,9 @@ import java.util.Map;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.boot.webmvc.error.ErrorAttributes;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
* A new {@link ErrorController}. * A new {@link ErrorController}.
* *
* @param errorAttributes the error attributes * @param errorAttributes the error attributes
* @param serverProperties configuration properties * @param webProperties web configuration properties
*/ */
public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
super(errorAttributes, serverProperties.getError()); super(errorAttributes, webProperties.getError());
} }
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.app.mgmt;
import static org.assertj.core.api.Assertions.assertThat; 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.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.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -59,11 +60,13 @@ class CorsTest extends AbstractSecurityTest {
.getContentAsString(); .getContentAsString();
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST); assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin( performOptionsRequestToUrlWithOrigin("/some_uri", ALLOWED_ORIGIN_FIRST)
"/some_uri", ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden()) // 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() .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
.getResponse().getContentAsString(); .getResponse().getContentAsString();
assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
} }
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception { private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {

View File

@@ -53,7 +53,7 @@
<!-- Spring - START --> <!-- Spring - START -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-webmvc</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@@ -109,7 +109,7 @@ public class MgmtSecurityConfiguration {
// could be used for instance to set authentication provider // 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 // 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<HttpSecurity> httpSecurityCustomizer, @Autowired(required = false) @Qualifier("hawkbitHttpSecurityCustomizer") final Customizer<HttpSecurity> httpSecurityCustomizer,
final SystemManagement systemManagement) throws Exception { final SystemManagement systemManagement) {
http http
.securityMatcher(MgmtRestConstants.REST + "/**") .securityMatcher(MgmtRestConstants.REST + "/**")
.authorizeHttpRequests(amrmRegistry -> amrmRegistry .authorizeHttpRequests(amrmRegistry -> amrmRegistry

View File

@@ -32,6 +32,11 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Database --> <!-- Database -->
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
@@ -58,9 +63,14 @@
<!-- Database END --> <!-- Database END -->
<!-- Test --> <!-- Test -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-security-test</artifactId> <artifactId>spring-boot-starter-security-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -14,9 +14,9 @@ import java.util.Map;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.boot.webmvc.error.ErrorAttributes;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
* A new {@link ErrorController}. * A new {@link ErrorController}.
* *
* @param errorAttributes the error attributes * @param errorAttributes the error attributes
* @param serverProperties configuration properties * @param webProperties web configuration properties
*/ */
public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
super(errorAttributes, serverProperties.getError()); super(errorAttributes, webProperties.getError());
} }
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.app;
import static org.assertj.core.api.Assertions.assertThat; 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.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.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -59,11 +60,13 @@ class CorsTest extends AbstractSecurityTest {
.getContentAsString(); .getContentAsString();
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST); assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin( performOptionsRequestToUrlWithOrigin("/some_uri", ALLOWED_ORIGIN_FIRST)
"/some_uri", ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden()) // 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() .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
.getResponse().getContentAsString(); .getResponse().getContentAsString();
assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
} }
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception { private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {

View File

@@ -22,6 +22,11 @@
<name>hawkBit :: Query Language :: JPA</name> <name>hawkBit :: Query Language :: JPA</name>
<dependencies> <dependencies>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.persistence</groupId> <groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId> <artifactId>org.eclipse.persistence.jpa</artifactId>
@@ -34,19 +39,9 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</exclusion>
</exclusions>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@@ -56,6 +51,11 @@
</dependency> </dependency>
<!-- Test --> <!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>

View File

@@ -22,8 +22,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor; 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.internal.SqmUtil;
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
import org.hibernate.query.sqm.sql.SqmTranslation; import org.hibernate.query.sqm.sql.SqmTranslation;
@@ -33,6 +35,7 @@ import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; 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.MutationStatement;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.select.SelectStatement;
@@ -60,14 +63,14 @@ public class HibernateUtils {
throw new UnsupportedOperationException("SqmTranslatorFactory resolver is not available"); throw new UnsupportedOperationException("SqmTranslatorFactory resolver is not available");
} }
final QuerySqmImpl<?> hqlQuery = query.unwrap(QuerySqmImpl.class); final SqmQueryImpl<?> hqlQuery = query.unwrap(SqmQueryImpl.class);
final SessionFactoryImplementor factory = hqlQuery.getSessionFactory(); final SessionFactoryImplementor sessionFactory = hqlQuery.getSessionFactory();
final SharedSessionContractImplementor session = hqlQuery.getSession(); final SharedSessionContractImplementor session = hqlQuery.getSession();
final SessionFactoryImplementor sessionFactory = session.getFactory(); final SqlAstCreationContext sqlAstCreationContext = (SqlAstCreationContext)session.getFactory();
final SqmTranslatorFactory sqmTranslatorFactory; final SqmTranslatorFactory sqmTranslatorFactory;
try { try {
sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(factory.getQueryEngine()); sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(sessionFactory.getQueryEngine());
} catch (final IllegalAccessException | InvocationTargetException e) { } catch (final IllegalAccessException | InvocationTargetException e) {
throw new UnsupportedOperationException("Can't create SqmTranslatorFactory", e); throw new UnsupportedOperationException("Can't create SqmTranslatorFactory", e);
} }
@@ -76,19 +79,20 @@ public class HibernateUtils {
hqlQuery.getSqmStatement() instanceof SqmSelectStatement<?> selectStatement hqlQuery.getSqmStatement() instanceof SqmSelectStatement<?> selectStatement
? sqmTranslatorFactory.createSelectTranslator(selectStatement, ? sqmTranslatorFactory.createSelectTranslator(selectStatement,
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(), hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
hqlQuery.getLoadQueryInfluencers(), sessionFactory, false) hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext, false)
: sqmTranslatorFactory.createMutationTranslator((SqmDmlStatement<?>) hqlQuery.getSqmStatement(), : sqmTranslatorFactory.createMutationTranslator((SqmDmlStatement<?>) hqlQuery.getSqmStatement(),
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(), hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
hqlQuery.getLoadQueryInfluencers(), sessionFactory); hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext);
final SqmTranslation<? extends Statement> sqmTranslation = sqmSelectTranslator.translate(); final SqmTranslation<? extends Statement> sqmTranslation = sqmSelectTranslator.translate();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref( final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
hqlQuery.getDomainParameterXref(), sqmTranslation::getJdbcParamsBySqmParam); hqlQuery.getDomainParameterXref(), sqmTranslation::getJdbcParamsBySqmParam);
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(hqlQuery.getQueryParameterBindings(), final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
hqlQuery.getDomainParameterXref(), jdbcParamsXref, factory.getRuntimeMetamodels().getMappingMetamodel(), (QueryParameterBindings) hqlQuery.getQueryParameterBindings(),
sqmSelectTranslator.getFromClauseAccess()::findTableGroup, new SqmParameterMappingModelResolutionAccess() { (DomainParameterXref) hqlQuery.getDomainParameterXref(), jdbcParamsXref, /*sessionFactory.getRuntimeMetamodels().getMappingMetamodel(),
sqmSelectTranslator.getFromClauseAccess()::findTableGroup,*/ new SqmParameterMappingModelResolutionAccess() {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -97,9 +101,9 @@ public class HibernateUtils {
} }
}, hqlQuery.getSession()); }, hqlQuery.getSession());
return (sqmTranslation.getSqlAst() instanceof SelectStatement selectStatement return (sqmTranslation.getSqlAst() instanceof SelectStatement selectStatement
? sqlAstTranslatorFactory.buildSelectTranslator(factory, selectStatement) ? sqlAstTranslatorFactory.buildSelectTranslator(sessionFactory, selectStatement)
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions()) .translate(jdbcParameterBindings, hqlQuery.getQueryOptions())
: sqlAstTranslatorFactory.buildMutationTranslator(factory, (MutationStatement) sqmTranslation.getSqlAst()) : sqlAstTranslatorFactory.buildMutationTranslator(sessionFactory, (MutationStatement) sqmTranslation.getSqlAst())
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions())) .translate(jdbcParameterBindings, hqlQuery.getQueryOptions()))
.getSqlString(); .getSqlString();
} }

View File

@@ -26,12 +26,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
@SuppressWarnings("java:S5961") // complex check because the matter is very complex @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 @EnableAutoConfiguration
@Slf4j @Slf4j
class SpecificationBuilderTest { class SpecificationBuilderTest {

View File

@@ -34,9 +34,10 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>org.springframework.data</groupId>
<artifactId>jackson-annotations</artifactId> <artifactId>spring-data-commons</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.cronutils</groupId> <groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId> <artifactId>cron-utils</artifactId>

View File

@@ -49,7 +49,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice; import org.springframework.data.domain.Slice;
import org.springframework.lang.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
/** /**

View File

@@ -27,7 +27,7 @@ public abstract class AbstractRemoteEvent extends ApplicationEvent {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final String id; private String id; // not a final - jackson 3 doesn't override finals
// for serialization libs like jackson // for serialization libs like jackson
protected AbstractRemoteEvent() { protected AbstractRemoteEvent() {

View File

@@ -25,7 +25,7 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
* Note: it implements {@link org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent} methods but in order * 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. * 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 @Getter
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
@@ -34,13 +34,13 @@ public abstract class RemoteIdEvent extends RemoteTenantAwareEvent {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Long entityId; private final Long entityId;
private String entityClass; private final String entityClass;
protected RemoteIdEvent(final String tenant, final Long entityId, final Class<? extends TenantAwareBaseEntity> entityClass) { protected RemoteIdEvent(final String tenant, final Long entityId, final Class<? extends TenantAwareBaseEntity> entityClass) {
super(tenant, entityId); super(tenant, entityId);
this.entityClass = entityClass.getName();
this.entityId = entityId; this.entityId = entityId;
this.entityClass = entityClass.getName();
} }
public String getCacheName() { public String getCacheName() {

View File

@@ -24,7 +24,7 @@ import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
* distributed events. All the necessary information of distributing events to * distributed events. All the necessary information of distributing events to
* other nodes. * other nodes.
*/ */
@NoArgsConstructor(access = AccessLevel.PROTECTED) // for serialization libs like jackson @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) // for serialization libs like jackson
@Getter @Getter
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
@@ -33,7 +33,7 @@ public class RemoteTenantAwareEvent extends AbstractRemoteEvent implements Tenan
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String tenant; private final String tenant;
/** /**
* Constructor. * Constructor.

View File

@@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.eclipse.hawkbit.repository.model.Action; 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}. * Defines the remote event of creating a new {@link Action}.

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -24,6 +25,7 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre
@Serial @Serial
private static final long serialVersionUID = 2L; private static final long serialVersionUID = 2L;
@JsonIgnore
public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) { public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
super(action, targetId, rolloutId, rolloutGroupId); super(action, targetId, rolloutId, rolloutGroupId);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -24,6 +25,7 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd
@Serial @Serial
private static final long serialVersionUID = 2L; private static final long serialVersionUID = 2L;
@JsonIgnore
public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) { public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
super(action, targetId, rolloutId, rolloutGroupId); super(action, targetId, rolloutId, rolloutGroupId);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -24,6 +25,7 @@ public class DistributionSetCreatedEvent extends RemoteEntityEvent<DistributionS
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetCreatedEvent(final DistributionSet distributionSet) { public DistributionSetCreatedEvent(final DistributionSet distributionSet) {
super(distributionSet); super(distributionSet);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -24,6 +25,7 @@ public class DistributionSetTagCreatedEvent extends RemoteEntityEvent<Distributi
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetTagCreatedEvent(final DistributionSetTag distributionSetTag) { public DistributionSetTagCreatedEvent(final DistributionSetTag distributionSetTag) {
super(distributionSetTag); super(distributionSetTag);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -24,6 +25,7 @@ public class DistributionSetTagUpdatedEvent extends RemoteEntityEvent<Distributi
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetTagUpdatedEvent(final DistributionSetTag distributionSetTag) { public DistributionSetTagUpdatedEvent(final DistributionSetTag distributionSetTag) {
super(distributionSetTag); super(distributionSetTag);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -25,6 +26,7 @@ public class DistributionSetTypeCreatedEvent extends RemoteEntityEvent<Distribut
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetTypeCreatedEvent(final DistributionSetType distributionSetType) { public DistributionSetTypeCreatedEvent(final DistributionSetType distributionSetType) {
super(distributionSetType); super(distributionSetType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.DistributionSetType;
@@ -26,6 +27,7 @@ public class DistributionSetTypeUpdatedEvent extends RemoteEntityEvent<Distribut
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetTypeUpdatedEvent(final DistributionSetType distributionSetType) { public DistributionSetTypeUpdatedEvent(final DistributionSetType distributionSetType) {
super(distributionSetType); super(distributionSetType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -28,6 +29,7 @@ public class DistributionSetUpdatedEvent extends RemoteEntityEvent<DistributionS
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public DistributionSetUpdatedEvent(final DistributionSet distributionSet) { public DistributionSetUpdatedEvent(final DistributionSet distributionSet) {
super(distributionSet); super(distributionSet);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
@@ -24,6 +25,7 @@ public class RolloutCreatedEvent extends RemoteEntityEvent<Rollout> implements E
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public RolloutCreatedEvent(final Rollout rollout) { public RolloutCreatedEvent(final Rollout rollout) {
super(rollout); super(rollout);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -25,6 +26,7 @@ public class RolloutGroupCreatedEvent extends AbstractRolloutGroupEvent implemen
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) { public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
super(rolloutGroup, rolloutId); super(rolloutGroup, rolloutId);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
@@ -24,6 +25,7 @@ public class RolloutGroupUpdatedEvent extends AbstractRolloutGroupEvent implemen
@Serial @Serial
private static final long serialVersionUID = 2L; private static final long serialVersionUID = 2L;
@JsonIgnore
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) { public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
super(rolloutGroup, rolloutId); super(rolloutGroup, rolloutId);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Rollout;
@@ -24,6 +25,7 @@ public class RolloutUpdatedEvent extends RemoteEntityEvent<Rollout> implements E
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public RolloutUpdatedEvent(final Rollout rollout) { public RolloutUpdatedEvent(final Rollout rollout) {
super(rollout); super(rollout);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -24,6 +25,7 @@ public class SoftwareModuleCreatedEvent extends RemoteEntityEvent<SoftwareModule
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public SoftwareModuleCreatedEvent(final SoftwareModule softwareModule) { public SoftwareModuleCreatedEvent(final SoftwareModule softwareModule) {
super(softwareModule); super(softwareModule);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -24,6 +25,7 @@ public class SoftwareModuleTypeCreatedEvent extends RemoteEntityEvent<SoftwareMo
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public SoftwareModuleTypeCreatedEvent(final SoftwareModuleType softwareModuleType) { public SoftwareModuleTypeCreatedEvent(final SoftwareModuleType softwareModuleType) {
super(softwareModuleType); super(softwareModuleType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -25,6 +26,7 @@ public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent<SoftwareMo
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public SoftwareModuleTypeUpdatedEvent(final SoftwareModuleType softwareModuleType) { public SoftwareModuleTypeUpdatedEvent(final SoftwareModuleType softwareModuleType) {
super(softwareModuleType); super(softwareModuleType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -25,6 +26,7 @@ public class SoftwareModuleUpdatedEvent extends RemoteEntityEvent<SoftwareModule
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public SoftwareModuleUpdatedEvent(final SoftwareModule softwareModule) { public SoftwareModuleUpdatedEvent(final SoftwareModule softwareModule) {
super(softwareModule); super(softwareModule);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -24,6 +25,7 @@ public class TargetCreatedEvent extends RemoteEntityEvent<Target> implements Ent
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetCreatedEvent(final Target target) { public TargetCreatedEvent(final Target target) {
super(target); super(target);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -24,6 +25,7 @@ public class TargetFilterQueryCreatedEvent extends RemoteEntityEvent<TargetFilte
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetFilterQueryCreatedEvent(final TargetFilterQuery targetFilterQuery) { public TargetFilterQueryCreatedEvent(final TargetFilterQuery targetFilterQuery) {
super(targetFilterQuery); super(targetFilterQuery);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
@@ -24,6 +25,7 @@ public class TargetFilterQueryUpdatedEvent extends RemoteEntityEvent<TargetFilte
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetFilterQueryUpdatedEvent(final TargetFilterQuery targetFilterQuery) { public TargetFilterQueryUpdatedEvent(final TargetFilterQuery targetFilterQuery) {
super(targetFilterQuery); super(targetFilterQuery);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
@@ -24,6 +25,7 @@ public class TargetTagCreatedEvent extends RemoteEntityEvent<TargetTag> implemen
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetTagCreatedEvent(final TargetTag targetTag) { public TargetTagCreatedEvent(final TargetTag targetTag) {
super(targetTag); super(targetTag);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetTag;
@@ -24,6 +25,7 @@ public class TargetTagUpdatedEvent extends RemoteEntityEvent<TargetTag> implemen
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetTagUpdatedEvent(final TargetTag targetTag) { public TargetTagUpdatedEvent(final TargetTag targetTag) {
super(targetTag); super(targetTag);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetType;
@@ -25,6 +26,7 @@ public class TargetTypeCreatedEvent extends RemoteEntityEvent<TargetType> implem
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetTypeCreatedEvent(final TargetType targetType) { public TargetTypeCreatedEvent(final TargetType targetType) {
super(targetType); super(targetType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetType;
@@ -25,6 +26,7 @@ public class TargetTypeUpdatedEvent extends RemoteEntityEvent<TargetType> implem
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetTypeUpdatedEvent(final TargetType targetType) { public TargetTypeUpdatedEvent(final TargetType targetType) {
super(targetType); super(targetType);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -24,6 +25,7 @@ public class TargetUpdatedEvent extends RemoteEntityEvent<Target> implements Ent
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TargetUpdatedEvent(final Target target) { public TargetUpdatedEvent(final Target target) {
super(target); super(target);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
@@ -24,6 +25,7 @@ public class TenantConfigurationCreatedEvent extends RemoteEntityEvent<TenantCon
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TenantConfigurationCreatedEvent(final TenantConfiguration tenantConfiguration) { public TenantConfigurationCreatedEvent(final TenantConfiguration tenantConfiguration) {
super(tenantConfiguration); super(tenantConfiguration);
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial; import java.io.Serial;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent; import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.TenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration;
@@ -25,6 +26,7 @@ public class TenantConfigurationUpdatedEvent extends RemoteEntityEvent<TenantCon
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@JsonIgnore
public TenantConfigurationUpdatedEvent(final TenantConfiguration tenantConfiguration) { public TenantConfigurationUpdatedEvent(final TenantConfiguration tenantConfiguration) {
super(tenantConfiguration); super(tenantConfiguration);
} }

View File

@@ -29,9 +29,10 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-context-support</artifactId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.protostuff</groupId> <groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId> <artifactId>protostuff-core</artifactId>
@@ -42,9 +43,5 @@
<artifactId>protostuff-runtime</artifactId> <artifactId>protostuff-runtime</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -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());
}
}

View File

@@ -9,30 +9,35 @@
*/ */
package org.eclipse.hawkbit.event; 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.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.converter.JacksonJsonMessageConverter;
import org.springframework.util.MimeType; 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 static final MimeType APPLICATION_REMOTE_EVENT_JSON = new MimeType("application", "remote-event-json");
public EventJacksonMessageConverter() { public EventJacksonMessageConverter(final JsonMapper mapper) {
super(APPLICATION_REMOTE_EVENT_JSON); super(mapper, APPLICATION_REMOTE_EVENT_JSON);
final ObjectMapper objectMapper = new ObjectMapper();
EventType.getNamedTypes().forEach(objectMapper::registerSubtypes);
setObjectMapper(objectMapper);
} }
@Override @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); return super.convertToInternal(payload, headers, conversionHint);
} }
@Override @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); return super.convertFromInternal(message, targetClass, conversionHint);
} }
} }

View File

@@ -17,6 +17,8 @@ import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema; import io.protostuff.runtime.RuntimeSchema;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent; 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.Message;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.AbstractMessageConverter; 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"); public static final MimeType APPLICATION_BINARY_PROTOSTUFF = new MimeType("application", "binary+protostuff");
private static final int HEADER_LENGTH_PREFIX_SIZE = 4; private static final int HEADER_LENGTH_PREFIX_SIZE = 4;
public EventProtoStuffMessageConverter() { public EventProtoStuffMessageConverter() {
super(APPLICATION_BINARY_PROTOSTUFF); super(APPLICATION_BINARY_PROTOSTUFF);
} }
@Override @Override
@NullMarked
protected boolean supports(final Class<?> aClass) { protected boolean supports(final Class<?> aClass) {
return AbstractRemoteEvent.class.isAssignableFrom(aClass); return AbstractRemoteEvent.class.isAssignableFrom(aClass);
} }
@Override @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(); final Object objectPayload = message.getPayload();
if (objectPayload instanceof byte[] payload) { if (objectPayload instanceof byte[] payload) {
final byte[] clazzHeader = extractClazzHeader(payload); final byte[] clazzHeader = extractClazzHeader(payload);
@@ -112,7 +116,6 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
return content; return content;
} }
private static EventType readClassHeader(final byte[] typeInformation) { private static EventType readClassHeader(final byte[] typeInformation) {
final Schema<EventType> schema = RuntimeSchema.getSchema(EventType.class); final Schema<EventType> schema = RuntimeSchema.getSchema(EventType.class);
final EventType deserializedType = schema.newMessage(); final EventType deserializedType = schema.newMessage();
@@ -134,8 +137,7 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
throw new MessageConversionException("Missing EventType for given class : " + clazz); throw new MessageConversionException("Missing EventType for given class : " + clazz);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") final Schema<Object> schema = (Schema<Object>) RuntimeSchema.getSchema((Class<?>) EventType.class);
final Schema<Object> schema = (Schema<Object>) RuntimeSchema.getSchema((Class<?>) EventType.class);
final LinkedBuffer buffer = LinkedBuffer.allocate(); final LinkedBuffer buffer = LinkedBuffer.allocate();
byte[] typeBytes = ProtobufIOUtil.toByteArray(clazzEventType, schema, buffer); byte[] typeBytes = ProtobufIOUtil.toByteArray(clazzEventType, schema, buffer);

View File

@@ -29,17 +29,20 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.MessageConverter;
import tools.jackson.databind.json.JsonMapper;
/** /**
* Autoconfiguration for the events. * Autoconfiguration for the events publishing.
*/ */
@Slf4j @Slf4j
@Configuration @Configuration
@Import(EventJacksonConfiguration.class)
public class EventPublisherConfiguration { public class EventPublisherConfiguration {
/** /**
@@ -108,15 +111,21 @@ public class EventPublisherConfiguration {
} }
@Bean @Bean
public Consumer<AbstractRemoteEvent> serviceEventConsumer(ApplicationEventPublisher publisher) { public Consumer<AbstractRemoteEvent> serviceEventConsumer(final ApplicationEventPublisher publisher) {
return publisher::publishEvent; return publisher::publishEvent;
} }
@Bean @Bean
public Consumer<AbstractRemoteEvent> fanoutEventConsumer(ApplicationEventPublisher publisher) { public Consumer<AbstractRemoteEvent> fanoutEventConsumer(final ApplicationEventPublisher publisher) {
return publisher::publishEvent; return publisher::publishEvent;
} }
@Bean
public MessageConverter eventJacksonMessageConverter(final JsonMapper mapper) {
return new EventJacksonMessageConverter(mapper);
}
@Configuration
@ConditionalOnClass({ Schema.class, ProtostuffIOUtil.class }) @ConditionalOnClass({ Schema.class, ProtostuffIOUtil.class })
protected static class EventProtostuffConfiguration { protected static class EventProtostuffConfiguration {
@@ -125,9 +134,4 @@ public class EventPublisherConfiguration {
return new EventProtoStuffMessageConverter(); return new EventProtoStuffMessageConverter();
} }
} }
@Bean
public MessageConverter eventJacksonMessageConverter() {
return new EventJacksonMessageConverter();
}
} }

View File

@@ -9,12 +9,10 @@
*/ */
package org.eclipse.hawkbit.event; package org.eclipse.hawkbit.event;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -72,15 +70,14 @@ import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceE
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent; import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetPollServiceEvent; import org.eclipse.hawkbit.repository.event.remote.service.TargetPollServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent; import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
import tools.jackson.databind.jsontype.NamedType;
/** /**
* The {@link EventType} class declares the event-type and it's corresponding * The {@link EventType} class declares the event-type, and it's corresponding encoding value in the payload of a remote header.
* encoding value in the payload of an remote header. The event-type is encoded * The event-type is encoded into the payload of the message which is distributed.
* into the payload of the message which is distributed. * <p/>
* * To encode and decode the event class type we need some conversation mapping between the actual class and the corresponding integer value
* To encode and decode the event class type we need some conversation mapping * which is the encoded value in the byte-payload.
* between the actual class and the corresponding integer value which is the
* encoded value in the byte-payload.
*/ */
// for marshalling and unmarshalling. // for marshalling and unmarshalling.
@NoArgsConstructor @NoArgsConstructor
@@ -93,8 +90,7 @@ public class EventType {
private int value; private int value;
// The associated event-type-value must remain the same as initially // The associated event-type-value must remain the same as initially declared. Otherwise, messages cannot correctly de-serialized.
// declared. Otherwise, messages cannot correctly de-serialized.
static { static {
// target // target
TYPES.put(1, TargetCreatedEvent.class); TYPES.put(1, TargetCreatedEvent.class);
@@ -215,9 +211,9 @@ public class EventType {
return TYPES.get(value); return TYPES.get(value);
} }
public static Collection<NamedType> getNamedTypes() { public static NamedType[] getNamedTypes() {
return TYPES.entrySet().stream() return TYPES.entrySet().stream()
.map(e -> new NamedType(e.getValue(), String.valueOf(e.getKey()))) .map(e -> new NamedType(e.getValue(), String.valueOf(e.getKey())))
.toList(); .toArray(NamedType[]::new);
} }
} }

View File

@@ -35,20 +35,14 @@ import org.springframework.messaging.converter.MessageConverter;
abstract class AbstractEventMessageConverterTest { abstract class AbstractEventMessageConverterTest {
protected final MessageConverter messageConverter;
@Mock @Mock
protected Message<Object> messageMock; protected Message<Object> messageMock;
@Mock @Mock
protected Target targetMock; protected Target targetMock;
@Mock @Mock
protected Action actionMock; protected Action actionMock;
AbstractEventMessageConverterTest(MessageConverter messageConverter) { protected abstract MessageConverter messageConverter();
this.messageConverter = messageConverter;
}
/** /**
* Verifies that the TargetCreatedEvent can be successfully serialized and deserialized * Verifies that the TargetCreatedEvent can be successfully serialized and deserialized
@@ -82,11 +76,8 @@ abstract class AbstractEventMessageConverterTest {
@Test @Test
void successfullySerializeAndDeserializeActionServiceEvent() { void successfullySerializeAndDeserializeActionServiceEvent() {
final ActionCreatedServiceEvent actionCreatedServiceEvent = final ActionCreatedServiceEvent actionCreatedServiceEvent = new ActionCreatedServiceEvent(createActionCreatedEvent());
new ActionCreatedServiceEvent(createActionCreatedEvent()); final ActionUpdatedServiceEvent actionUpdatedServiceEvent = new ActionUpdatedServiceEvent(createActionUpdatedEvent());
final ActionUpdatedServiceEvent actionUpdatedServiceEvent =
new ActionUpdatedServiceEvent(createActionUpdatedEvent());
assertSerializeAndDeserialize(actionCreatedServiceEvent, ActionCreatedServiceEvent.class); assertSerializeAndDeserialize(actionCreatedServiceEvent, ActionCreatedServiceEvent.class);
assertSerializeAndDeserialize(actionUpdatedServiceEvent, ActionUpdatedServiceEvent.class); assertSerializeAndDeserialize(actionUpdatedServiceEvent, ActionUpdatedServiceEvent.class);
@@ -112,7 +103,9 @@ abstract class AbstractEventMessageConverterTest {
return new ActionUpdatedEvent(actionMock, 1L, 2L, 3L); return new ActionUpdatedEvent(actionMock, 1L, 2L, 3L);
} }
<T extends AbstractRemoteEvent> void assertSerializeAndDeserialize(T event, Class<? extends AbstractRemoteEvent> expectedClass) { private <T extends AbstractRemoteEvent> void assertSerializeAndDeserialize(
final T event, final Class<? extends AbstractRemoteEvent> expectedClass) {
final MessageConverter messageConverter = messageConverter();
// serialize // serialize
Object serializedEvent = null; Object serializedEvent = null;
if (messageConverter instanceof EventProtoStuffMessageConverter protoStuff) { if (messageConverter instanceof EventProtoStuffMessageConverter protoStuff) {
@@ -135,4 +128,4 @@ abstract class AbstractEventMessageConverterTest {
.isInstanceOf(expectedClass) .isInstanceOf(expectedClass)
.isEqualTo(event); .isEqualTo(event);
} }
} }

View File

@@ -9,13 +9,27 @@
*/ */
package org.eclipse.hawkbit.event; package org.eclipse.hawkbit.event;
import java.util.Objects;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.messaging.converter.MessageConverter;
import tools.jackson.databind.json.JsonMapper;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@SpringBootTest(classes = EventJacksonConfiguration.class)
class EventJacksonMessageConverterTest extends AbstractEventMessageConverterTest { class EventJacksonMessageConverterTest extends AbstractEventMessageConverterTest {
EventJacksonMessageConverterTest() { private MessageConverter messageConverter;
super(new EventJacksonMessageConverter());
@Autowired
void setJsonMapper(final JsonMapper jsonMapper) {
messageConverter = new EventJacksonMessageConverter(jsonMapper);
}
protected MessageConverter messageConverter() {
return Objects.requireNonNull(messageConverter, "MessageConverter has not been initialized");
} }
} }

View File

@@ -21,12 +21,15 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class EventProtoStuffMessageConverterTest extends AbstractEventMessageConverterTest { class EventProtoStuffMessageConverterTest extends AbstractEventMessageConverterTest {
EventProtoStuffMessageConverterTest() { private static final EventProtoStuffMessageConverter MESSAGE_CONVERTER = new EventProtoStuffMessageConverter();
super(new EventProtoStuffMessageConverter());
protected MessageConverter messageConverter() {
return MESSAGE_CONVERTER;
} }
/** /**
@@ -39,7 +42,7 @@ class EventProtoStuffMessageConverterTest extends AbstractEventMessageConverterT
assertThatExceptionOfType(MessageConversionException.class) assertThatExceptionOfType(MessageConversionException.class)
.as("Missing MessageConversationException for un-defined event-type") .as("Missing MessageConversationException for un-defined event-type")
.isThrownBy(() -> ((EventProtoStuffMessageConverter)messageConverter).convertToInternal(dummyEvent, messageHeaders, null)); .isThrownBy(() -> MESSAGE_CONVERTER.convertToInternal(dummyEvent, messageHeaders, null));
} }
/** /**

View File

@@ -41,12 +41,6 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -33,24 +33,23 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.micrometer</groupId> <groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId> <artifactId>micrometer-core</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- Static class generation --> <dependency> <!-- Static class generation -->
<dependency>
<groupId>org.hibernate.orm</groupId> <groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId> <artifactId>hibernate-processor</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
@@ -71,13 +70,6 @@
<configuration> <configuration>
<basePackage>org.eclipse.hawkbit.repository.jpa.model</basePackage> <basePackage>org.eclipse.hawkbit.repository.jpa.model</basePackage>
</configuration> </configuration>
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>${eclipselink.version}</version>
</dependency>
</dependencies>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

View File

@@ -17,7 +17,7 @@ import jakarta.persistence.PersistenceException;
import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator; import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; 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.JpaSystemException;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;

View File

@@ -17,10 +17,10 @@ import javax.sql.DataSource;
import lombok.Data; import lombok.Data;
import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.springframework.beans.factory.ObjectProvider; 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.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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@@ -38,7 +38,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
public class JpaConfiguration extends JpaBaseConfiguration { public class JpaConfiguration extends JpaBaseConfiguration {
@Data @Data
@ConfigurationProperties // predix is "/" intentionally @ConfigurationProperties // prefix is "/" intentionally
protected static class Properties { protected static class Properties {
private final Map<String, String> eclipselink = new HashMap<>(); private final Map<String, String> eclipselink = new HashMap<>();

View File

@@ -42,9 +42,9 @@ import org.springframework.scheduling.annotation.Scheduled;
* <ol> * <ol>
* <li>The Spring property spring.jpa.properties.eclipselink.profiler=PerformanceMonitor shall be set - enables Eclipselink statistics * <li>The Spring property spring.jpa.properties.eclipselink.profiler=PerformanceMonitor shall be set - enables Eclipselink statistics
* collecting</li> * collecting</li>
* <li>By default the periodic stdout log is disabled by setting hawkbit.jpa.statistics.dump-period-ms=9223372036854775807 (Long.MAX_VALUE) - * <li>By default, the periodic stdout log is disabled by setting hawkbit.jpa.statistics.dump-period-ms=9223372036854775807 (Long.MAX_VALUE) -
* i.e. effectively <b>never</b>. If log is required it should be set to the required period</li> * i.e. effectively <b>never</b>. If log is required it should be set to the required period</li>
* <li>The MeterRegistry shall be registered available - e.g. include org.springframework.boot:spring-boot-actuator-autoconfigure</li> * <li>The MeterRegistry shall be registered and available</li>
* <li>(?) When using in test the metrics MAYBE shall be enabled with @AutoConfigureObservability(tracing = false)</li> * <li>(?) When using in test the metrics MAYBE shall be enabled with @AutoConfigureObservability(tracing = false)</li>
* </ol> * </ol>
* *

View File

@@ -24,16 +24,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId> <artifactId>spring-boot-starter-flyway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -10,7 +10,7 @@
package org.eclipse.hawkbit.autoconfigure.repository.jpa.flyway; package org.eclipse.hawkbit.autoconfigure.repository.jpa.flyway;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; 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.Configuration;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;

View File

@@ -0,0 +1 @@
ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER TIMESTAMP NOT NULL;

View File

@@ -0,0 +1 @@
ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER DATETIME(6) NOT NULL;

View File

@@ -0,0 +1 @@
ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER TIMESTAMP NOT NULL;

Some files were not shown because too many files have changed in this diff Show More