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:
@@ -27,26 +27,5 @@
|
||||
<artifactId>hawkbit-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
</project>
|
||||
@@ -27,18 +27,5 @@
|
||||
<artifactId>hawkbit-artifact-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
</project>
|
||||
@@ -19,7 +19,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
|
||||
import org.eclipse.hawkbit.artifact.ArtifactStorage;
|
||||
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
|
||||
@@ -47,7 +46,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
|
||||
|
||||
@Override
|
||||
public void deleteBySha1(final String tenant, final String sha1) {
|
||||
FileUtils.deleteQuietly(getFile(tenant, sha1));
|
||||
deleteSilent(getFile(tenant, sha1));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +64,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
|
||||
|
||||
@Override
|
||||
public void deleteByTenant(final String tenant) {
|
||||
FileUtils.deleteQuietly(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
|
||||
deleteSilent(Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant)).toFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +77,7 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
|
||||
throws IOException {
|
||||
final File fileSHA1Naming = getFile(tenant, base16Hashes.sha1());
|
||||
if (fileSHA1Naming.exists()) {
|
||||
FileUtils.deleteQuietly(tempFile);
|
||||
deleteSilent(tempFile);
|
||||
} else {
|
||||
Files.move(tempFile.toPath(), fileSHA1Naming.toPath());
|
||||
}
|
||||
@@ -107,4 +106,24 @@ public class FileArtifactStorage extends AbstractArtifactStorage {
|
||||
final String folder2 = sha1.substring(length - 2, length);
|
||||
return Paths.get(artifactResourceProperties.getPath(), sanitizeTenant(tenant), folder1, folder2);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "java:S899", "java:S4042" }) // just ignore the result - silent
|
||||
private static void deleteSilent(final File file) {
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory()) {
|
||||
// delete children
|
||||
final File[] children = file.listFiles();
|
||||
if (children != null) {
|
||||
for (final File child : children) {
|
||||
deleteSilent(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
file.delete(); // silent
|
||||
} catch (final Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.eclipse.hawkbit.artifact.AbstractArtifactStorage;
|
||||
import org.eclipse.hawkbit.artifact.exception.ArtifactBinaryNotFoundException;
|
||||
@@ -54,7 +54,7 @@ class FileArtifactStorageTest {
|
||||
static void afterClass() {
|
||||
if (new File(artifactResourceProperties.getPath()).exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(new File(artifactResourceProperties.getPath()));
|
||||
delete(new File(artifactResourceProperties.getPath()));
|
||||
} catch (final IOException | IllegalArgumentException e) {
|
||||
log.warn("Cannot delete file-directory", e);
|
||||
}
|
||||
@@ -69,9 +69,10 @@ class FileArtifactStorageTest {
|
||||
final byte[] fileContent = randomBytes();
|
||||
final StoredArtifactInfo artifact = storeRandomArtifact(fileContent);
|
||||
|
||||
final byte[] readContent = new byte[fileContent.length];
|
||||
IOUtils.read(artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1()), readContent);
|
||||
assertThat(readContent).isEqualTo(fileContent);
|
||||
try (final InputStream is = artifactFilesystemRepository.getBySha1(TENANT, artifact.getHashes().sha1())) {
|
||||
final byte[] readContent = is.readAllBytes();
|
||||
assertThat(readContent).isEqualTo(fileContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,4 +142,20 @@ class FileArtifactStorageTest {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,13 +44,6 @@
|
||||
<artifactId>protostuff-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.protostuff</groupId>
|
||||
<artifactId>protostuff-runtime</artifactId>
|
||||
|
||||
@@ -18,8 +18,8 @@ import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.security.autoconfigure.SecurityProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@@ -11,8 +11,8 @@ package org.eclipse.hawkbit.autoconfigure.security;
|
||||
|
||||
import org.eclipse.hawkbit.auth.StaticAuthenticationProvider;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.security.autoconfigure.SecurityProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
@@ -24,72 +24,43 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<artifactId>spring-boot-starter-security-oauth2-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-core</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 -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>tools.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</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>
|
||||
</project>
|
||||
@@ -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 {}
|
||||
@@ -20,7 +20,7 @@ import java.util.regex.Pattern;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUser;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareUserProperties;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.security.autoconfigure.SecurityProperties;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
@@ -20,8 +20,6 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -36,6 +34,7 @@ import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* A 'static' class providing methods related to access context:
|
||||
@@ -298,11 +297,7 @@ public class AccessContext {
|
||||
@SuppressWarnings("java:S112") // java:S112 - generic method
|
||||
private static String serialize(final SecurityContext securityContext) {
|
||||
Objects.requireNonNull(securityContext);
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
|
||||
} catch (final JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return OBJECT_MAPPER.writeValueAsString(new SecCtxInfo(securityContext));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,12 +309,7 @@ public class AccessContext {
|
||||
@SuppressWarnings("java:S112") // java:S112 - generic method
|
||||
private static SecurityContext deserialize(final String securityContextString) {
|
||||
Objects.requireNonNull(securityContextString);
|
||||
final String securityContextTrimmed = securityContextString.trim();
|
||||
try {
|
||||
return OBJECT_MAPPER.readerFor(SecCtxInfo.class).<SecCtxInfo> readValue(securityContextTrimmed).toSecurityContext();
|
||||
} catch (final JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return OBJECT_MAPPER.readerFor(SecCtxInfo.class).<SecCtxInfo> readValue(securityContextString.trim()).toSecurityContext();
|
||||
}
|
||||
|
||||
private static boolean isAuthenticationInvalid(final Authentication authentication) {
|
||||
|
||||
@@ -14,31 +14,29 @@ import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
|
||||
import io.micrometer.common.KeyValue;
|
||||
import io.micrometer.common.KeyValues;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import lombok.NonNull;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.data.DefaultRepositoryTagsProvider;
|
||||
import org.springframework.boot.actuate.metrics.data.RepositoryTagsProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.data.metrics.DefaultRepositoryTagsProvider;
|
||||
import org.springframework.boot.data.metrics.RepositoryTagsProvider;
|
||||
import org.springframework.boot.security.autoconfigure.web.servlet.SecurityFilterProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener;
|
||||
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
|
||||
import org.springframework.http.server.observation.ServerRequestObservationContext;
|
||||
import org.springframework.http.server.observation.ServerRequestObservationConvention;
|
||||
import org.springframework.web.filter.ServerHttpObservationFilter;
|
||||
|
||||
@AutoConfiguration
|
||||
@@ -57,11 +55,11 @@ public class DefaultTenantConfiguration {
|
||||
}
|
||||
|
||||
@AutoConfiguration(afterName = {
|
||||
"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration",
|
||||
"org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration" })
|
||||
"org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration",
|
||||
"org.springframework.boot.security.autoconfigure.SecurityAutoConfiguration" })
|
||||
@ConditionalOnProperty(name = "hawkbit.metrics.tenancy.web.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnClass(name = { "org.springframework.web.servlet.DispatcherServlet", "io.micrometer.observation.Observation" })
|
||||
@ConditionalOnClass(name = { "io.micrometer.observation.Observation", "org.springframework.web.servlet.DispatcherServlet" })
|
||||
@ConditionalOnBean(ObservationRegistry.class)
|
||||
public static class WebConfig {
|
||||
|
||||
@@ -87,31 +85,40 @@ public class DefaultTenantConfiguration {
|
||||
@Primary
|
||||
public FilterRegistrationBean<ServerHttpObservationFilter> webMvcObservationFilter(
|
||||
final ObservationRegistry registry,
|
||||
// should be serverRequestObservationConvention (registered above)
|
||||
final ObjectProvider<ServerRequestObservationConvention> customConvention,
|
||||
final ObservationProperties observationProperties,
|
||||
final SecurityProperties securityProperties) {
|
||||
final FilterRegistrationBean<ServerHttpObservationFilter> filterRegistrationBean = new WebMvcObservationAutoConfiguration()
|
||||
.webMvcObservationFilter(registry, customConvention, observationProperties);
|
||||
final SecurityFilterProperties securityFilterProperties) {
|
||||
final FilterRegistrationBean<ServerHttpObservationFilter> filterRegistrationBean = new FilterRegistrationBean<>(
|
||||
new ServerHttpObservationFilter(registry, new DefaultServerRequestObservationConvention() {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public KeyValues getLowCardinalityKeyValues(@NonNull final ServerRequestObservationContext context) {
|
||||
// Make sure that KeyValues entries are already sorted by name for better performance
|
||||
return KeyValues.of(exception(context), method(context), outcome(context), status(context), tenant(), uri(context));
|
||||
}
|
||||
|
||||
private static KeyValue tenant() {
|
||||
return KeyValue.of(TENANT_TAG, Optional.ofNullable(AccessContext.tenant()).orElse("n/a"));
|
||||
}
|
||||
}));
|
||||
filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD);
|
||||
// after security filter, so to be able to log tenant
|
||||
filterRegistrationBean.setOrder(securityProperties.getFilter().getOrder() + 1);
|
||||
filterRegistrationBean.setOrder(securityFilterProperties.getOrder() + 1);
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
}
|
||||
|
||||
@AutoConfiguration(afterName = "org.eclipse.hawkbit.autoconfigure.security.SecurityAutoConfiguration")
|
||||
@AutoConfiguration(afterName = "org.springframework.boot.security.autoconfigure.SecurityAutoConfiguration")
|
||||
@ConditionalOnClass(name = "org.springframework.boot.data.metrics.DefaultRepositoryTagsProvider")
|
||||
@ConditionalOnProperty(name = "hawkbit.metrics.tenancy.repository.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnClass(name = {
|
||||
"io.micrometer.core.instrument.Tag",
|
||||
"org.springframework.data.repository.core.support.RepositoryMethodInvocationListener" })
|
||||
public static class RepositoryConfig {
|
||||
|
||||
@Bean
|
||||
public RepositoryTagsProvider repositoryTagsProvider() {
|
||||
return new DefaultRepositoryTagsProvider() {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Iterable<Tag> repositoryTags(final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) {
|
||||
public Iterable<Tag> repositoryTags(@NonNull final RepositoryMethodInvocationListener.RepositoryMethodInvocation invocation) {
|
||||
final Iterable<Tag> defaultTags = super.repositoryTags(invocation);
|
||||
final String tenant = TENANT_TAG_VALUE_PROVIDER.get();
|
||||
return () -> {
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.springframework.cache.caffeine.CaffeineCache;
|
||||
import org.springframework.cache.support.AbstractValueAdaptingCache;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A spring Cache Manager that handles the multi tenancy.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
org.eclipse.hawkbit.HawkbitAutoConfiguration
|
||||
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration
|
||||
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.WebConfig
|
||||
org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration.RepositoryConfig
|
||||
|
||||
@@ -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
|
||||
@@ -14,7 +14,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<groupId>tools.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-cbor</artifactId>
|
||||
</dependency>
|
||||
|
||||
@@ -64,5 +64,11 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webmvc-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -18,20 +18,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
|
||||
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
|
||||
import com.fasterxml.jackson.dataformat.cbor.CBORParser;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
|
||||
import org.eclipse.hawkbit.ddi.json.model.DdiAssignedVersion;
|
||||
@@ -54,6 +45,11 @@ import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import tools.jackson.core.JsonGenerator;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.json.JsonFactory;
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
import tools.jackson.dataformat.cbor.CBORFactory;
|
||||
|
||||
@ContextConfiguration(
|
||||
classes = { DdiApiConfiguration.class, RestConfiguration.class, JpaRepositoryConfiguration.class, TestConfiguration.class })
|
||||
@@ -86,14 +82,13 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
*
|
||||
* @param json JSON object to convert
|
||||
* @return Equivalent CBOR data
|
||||
* @throws IOException Invalid JSON input
|
||||
*/
|
||||
protected static byte[] jsonToCbor(final String json) throws IOException {
|
||||
protected static byte[] jsonToCbor(final String json) {
|
||||
final JsonFactory jsonFactory = new JsonFactory();
|
||||
final JsonParser jsonParser = jsonFactory.createParser(json);
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
final CBORFactory cborFactory = new CBORFactory();
|
||||
final CBORGenerator cborGenerator = cborFactory.createGenerator(out);
|
||||
final JsonGenerator cborGenerator = cborFactory.createGenerator(out);
|
||||
while (jsonParser.nextToken() != null) {
|
||||
cborGenerator.copyCurrentEvent(jsonParser);
|
||||
}
|
||||
@@ -106,11 +101,9 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
*
|
||||
* @param input CBOR data to convert
|
||||
* @return Equivalent JSON string
|
||||
* @throws IOException Invalid CBOR input
|
||||
*/
|
||||
protected static String cborToJson(final byte[] input) throws IOException {
|
||||
final CBORFactory cborFactory = new CBORFactory();
|
||||
final CBORParser cborParser = cborFactory.createParser(input);
|
||||
protected static String cborToJson(final byte[] input) {
|
||||
final JsonParser cborParser = new CBORFactory().createParser(input);
|
||||
final JsonFactory jsonFactory = new JsonFactory();
|
||||
final StringWriter stringWriter = new StringWriter();
|
||||
final JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
|
||||
@@ -210,107 +203,92 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat
|
||||
controllerId + "/deploymentBase/" + actionId;
|
||||
}
|
||||
|
||||
protected String getJsonRejectedCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
|
||||
protected String getJsonRejectedCancelActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("rejected"));
|
||||
}
|
||||
|
||||
protected String getJsonRejectedDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
|
||||
protected String getJsonRejectedDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.REJECTED, DdiResult.FinalResult.NONE, Collections.singletonList("rejected"));
|
||||
}
|
||||
|
||||
protected String getJsonDownloadDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
|
||||
protected String getJsonDownloadDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
|
||||
}
|
||||
|
||||
protected String getJsonDownloadedDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
|
||||
protected String getJsonDownloadedDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, Collections.singletonList("download"));
|
||||
}
|
||||
|
||||
protected String getJsonCanceledCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
|
||||
protected String getJsonCanceledCancelActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("canceled"));
|
||||
}
|
||||
|
||||
protected String getJsonCanceledDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
|
||||
protected String getJsonCanceledDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CANCELED, DdiResult.FinalResult.NONE, Collections.singletonList("canceled"));
|
||||
}
|
||||
|
||||
protected String getJsonScheduledCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("scheduled"));
|
||||
protected String getJsonScheduledCancelActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.SUCCESS,
|
||||
Collections.singletonList("scheduled"));
|
||||
}
|
||||
|
||||
protected String getJsonScheduledDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
|
||||
protected String getJsonScheduledDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.SCHEDULED, DdiResult.FinalResult.NONE, Collections.singletonList("scheduled"));
|
||||
}
|
||||
|
||||
protected String getJsonResumedCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
|
||||
protected String getJsonResumedCancelActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("resumed"));
|
||||
}
|
||||
|
||||
protected String getJsonResumedDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
|
||||
protected String getJsonResumedDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.RESUMED, DdiResult.FinalResult.NONE, Collections.singletonList("resumed"));
|
||||
}
|
||||
|
||||
protected String getJsonProceedingCancelActionFeedback() throws JsonProcessingException {
|
||||
protected String getJsonProceedingCancelActionFeedback() {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.SUCCESS, Collections.singletonList("proceeding"));
|
||||
}
|
||||
|
||||
protected String getJsonProceedingDeploymentActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
|
||||
protected String getJsonProceedingDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, Collections.singletonList("proceeding"));
|
||||
}
|
||||
|
||||
protected String getJsonClosedCancelActionFeedback() throws JsonProcessingException {
|
||||
return getJsonActionFeedback(
|
||||
DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("closed"));
|
||||
protected String getJsonClosedCancelActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.SUCCESS, Collections.singletonList("closed"));
|
||||
}
|
||||
|
||||
protected String getJsonClosedDeploymentActionFeedback() throws JsonProcessingException {
|
||||
protected String getJsonClosedDeploymentActionFeedback() {
|
||||
return getJsonActionFeedback(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.NONE, Collections.singletonList("closed"));
|
||||
}
|
||||
|
||||
protected String getJsonActionFeedback(
|
||||
final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) throws JsonProcessingException {
|
||||
protected String getJsonActionFeedback(final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult) {
|
||||
return getJsonActionFeedback(executionStatus, finalResult, Collections.singletonList(randomString(1000)));
|
||||
}
|
||||
|
||||
protected String getJsonActionFeedback(
|
||||
final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult,
|
||||
final List<String> messages) throws JsonProcessingException {
|
||||
final DdiStatus.ExecutionStatus executionStatus, final DdiResult ddiResult, final List<String> messages) {
|
||||
final DdiStatus ddiStatus = new DdiStatus(executionStatus, ddiResult, null, messages);
|
||||
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
|
||||
}
|
||||
|
||||
protected String getJsonActionFeedback(
|
||||
final DdiStatus.ExecutionStatus executionStatus,
|
||||
final DdiResult.FinalResult finalResult, final List<String> messages) throws JsonProcessingException {
|
||||
final DdiStatus.ExecutionStatus executionStatus, final DdiResult.FinalResult finalResult, final List<String> messages) {
|
||||
return getJsonActionFeedback(executionStatus, finalResult, null, messages);
|
||||
}
|
||||
|
||||
protected String getJsonActionFeedback(
|
||||
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);
|
||||
return OBJECT_MAPPER.writeValueAsString(new DdiActionFeedback(ddiStatus));
|
||||
}
|
||||
|
||||
protected String getJsonConfirmationFeedback(
|
||||
final DdiConfirmationFeedback.Confirmation confirmation,
|
||||
final Integer code, final List<String> messages) throws JsonProcessingException {
|
||||
final DdiConfirmationFeedback.Confirmation confirmation, final Integer code, final List<String> 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<!-- Spring - START -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -76,7 +76,7 @@ class ControllerDownloadSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@Order(300) // higher priority than HawkBit DDI security, so that the DDI DL security is applied first
|
||||
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) throws Exception {
|
||||
protected SecurityFilterChain filterChainDDIDL(final HttpSecurity http) {
|
||||
http
|
||||
.securityMatcher(DDI_DL_ANT_MATCHER)
|
||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry.anyRequest().authenticated())
|
||||
|
||||
@@ -40,11 +40,7 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit</artifactId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
|
||||
@@ -184,7 +184,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
|
||||
protected void sendPingResponseToDmfReceiver(final Message ping, final String tenant, final String virtualHost) {
|
||||
final Message message = MessageBuilder
|
||||
.withBody(String.valueOf(java.lang.System.currentTimeMillis()).getBytes())
|
||||
.withBody(String.valueOf(System.currentTimeMillis()).getBytes())
|
||||
.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
|
||||
.setCorrelationId(ping.getMessageProperties().getCorrelationId())
|
||||
.setHeader(MessageHeaderKey.TYPE, MessageType.PING_RESPONSE)
|
||||
@@ -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
|
||||
final Target firstTarget = targets.get(0);
|
||||
final DmfBatchDownloadAndUpdateRequest batchRequest = new DmfBatchDownloadAndUpdateRequest(
|
||||
java.lang.System.currentTimeMillis(),
|
||||
System.currentTimeMillis(),
|
||||
dmfTargets,
|
||||
Optional.ofNullable(modules)
|
||||
.map(Map::entrySet)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
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.RabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
|
||||
import org.springframework.boot.amqp.autoconfigure.SimpleRabbitListenerContainerFactoryConfigurer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -42,9 +43,10 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.core.retry.RetryPolicy;
|
||||
import org.springframework.core.retry.RetryTemplate;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
/**
|
||||
* Spring configuration for AMQP based DMF communication for indirect device integration.
|
||||
@@ -91,13 +93,16 @@ public class DmfApiConfiguration {
|
||||
* @return {@link RabbitTemplate} with automatic retry, published confirms and {@link Jackson2JsonMessageConverter}.
|
||||
*/
|
||||
@Bean
|
||||
public RabbitTemplate rabbitTemplate() {
|
||||
public RabbitTemplate rabbitTemplate(final JsonMapper jsonMapper) {
|
||||
final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
|
||||
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
|
||||
rabbitTemplate.setMessageConverter(new JacksonJsonMessageConverter(jsonMapper));
|
||||
|
||||
final RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
|
||||
rabbitTemplate.setRetryTemplate(retryTemplate);
|
||||
// the same policy the previously used default ExponentialBackOffPolicy applied
|
||||
rabbitTemplate.setRetryTemplate(new RetryTemplate(RetryPolicy.builder()
|
||||
.delay(Duration.ofMillis(100))
|
||||
.multiplier(2)
|
||||
.maxDelay(Duration.ofSeconds(30))
|
||||
.build()));
|
||||
|
||||
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
|
||||
if (ack) {
|
||||
@@ -136,8 +141,8 @@ public class DmfApiConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AmqpMessageSenderService amqpSenderServiceBean() {
|
||||
return new DefaultAmqpMessageSenderService(rabbitTemplate());
|
||||
public AmqpMessageSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
|
||||
return new DefaultAmqpMessageSenderService(rabbitTemplate);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,5 +208,4 @@ public class DmfApiConfiguration {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,7 +54,7 @@ import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.JacksonJsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConversionException;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
|
||||
@@ -108,7 +108,7 @@ class AmqpMessageHandlerServiceTest {
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
void before() {
|
||||
TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagement);
|
||||
messageConverter = new Jackson2JsonMessageConverter();
|
||||
messageConverter = new JacksonJsonMessageConverter();
|
||||
lenient().when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
||||
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(
|
||||
|
||||
@@ -751,7 +751,6 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr
|
||||
// retrieve action and ensure that it is in Running state
|
||||
final Action action = deploymentManagement.findAction(actionId).orElseThrow(() -> new AssertionError("Action not found!"));
|
||||
assertThat(action.getStatus()).isEqualTo(Status.RUNNING);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
2
hawkbit-dmf/hawkbit-dmf-api/lombok.config
Normal file
2
hawkbit-dmf/hawkbit-dmf-api/lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
# Disable nullability annotations in order to do not bring jspecify as a dependency
|
||||
lombok.addNullAnnotations=none
|
||||
@@ -52,18 +52,11 @@
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>http-client</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit-test</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
|
||||
@@ -45,11 +45,6 @@
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.mcp.server;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
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.
|
||||
|
||||
@@ -31,5 +31,11 @@
|
||||
<artifactId>hawkbit-rest-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-hateoas-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
|
||||
@@ -17,6 +18,7 @@ import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
/**
|
||||
* Request Body of DistributionSet for assignment operations (ID only).
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
||||
@@ -41,13 +41,11 @@ class MgmtTargetAssignmentResponseBodyTest {
|
||||
|
||||
assertThat(jsonNode.has("assigned")).as("the assigned targets count").isTrue();
|
||||
assertThat(jsonNode.get("assigned").isNumber()).as("the assigned targets count").isTrue();
|
||||
assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count")
|
||||
.isEqualTo(ASSIGNED_ACTIONS.size());
|
||||
assertThat(jsonNode.get("assigned").asLong()).as("the assigned targets count").isEqualTo(ASSIGNED_ACTIONS.size());
|
||||
|
||||
assertThat(jsonNode.has("alreadyAssigned")).as("the already assigned targets count").isTrue();
|
||||
assertThat(jsonNode.get("alreadyAssigned").isNumber()).as("the already assigned targets count").isTrue();
|
||||
assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count")
|
||||
.isEqualTo(ALREADY_ASSIGNED_COUNT);
|
||||
assertThat(jsonNode.get("alreadyAssigned").asLong()).as("the already assigned targets count").isEqualTo(ALREADY_ASSIGNED_COUNT);
|
||||
|
||||
assertThat(jsonNode.has("total")).as("the total targets count").isTrue();
|
||||
assertThat(jsonNode.get("total").isNumber()).as("the total targets count").isTrue();
|
||||
|
||||
@@ -59,5 +59,11 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webmvc-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -17,6 +17,7 @@ import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Enable {@link ComponentScan} in the resource package to set up all {@link Controller} annotated classes and set up the REST-Resources
|
||||
@@ -27,4 +28,4 @@ import org.springframework.stereotype.Controller;
|
||||
@ComponentScan
|
||||
@Import({ RestConfiguration.class, OpenApi.class })
|
||||
@PropertySource("classpath:/hawkbit-mgmt-api-defaults.properties")
|
||||
public class MgmtApiConfiguration {}
|
||||
public class MgmtApiConfiguration implements WebMvcConfigurer {}
|
||||
@@ -32,8 +32,8 @@ import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
@@ -92,7 +92,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -145,8 +145,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
ActionRepository actionRepository;
|
||||
@Autowired
|
||||
private JpaProperties jpaProperties;
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Ensures that when targetType value of -1 is provided the target type is unassigned from the target.
|
||||
@@ -254,7 +252,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
final MgmtTargetAutoConfirmUpdate body = new MgmtTargetAutoConfirmUpdate("custom_initiator_value", "custom_remark_value");
|
||||
mvc.perform(post(TARGETS_V1 + "/{targetId}/" + AUTO_CONFIRM + "/" + ACTIVATE,
|
||||
testTarget.getControllerId())
|
||||
.content(objectMapper.writeValueAsString(body)).contentType(APPLICATION_JSON))
|
||||
.content(OBJECT_MAPPER.writeValueAsString(body)).contentType(APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
@@ -571,13 +571,13 @@ class MgmtTargetTypeResourceTest extends AbstractManagementApiIntegrationTest {
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("{\"id\":1}")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
// actually it is semi invalid could become bad request - not strict but yet accepted
|
||||
// single element is assumed to be array of single element see {@link MgmtApiConfiguration#configureMessageConverters}
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).contentType(MediaType.APPLICATION_JSON).content("{\"id\":44456}"))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isBadRequest());
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).content("[{\"id\":44456}]")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
mvc.perform(post(TARGETTYPE_DSTYPES_ENDPOINT, testType.getId()).contentType(MediaType.APPLICATION_JSON).content("[{\"id\":44456}]"))
|
||||
.andDo(MockMvcResultPrinter.print())
|
||||
.andExpect(status().isNotFound());
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||
import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
|
||||
import org.springframework.boot.webmvc.error.ErrorAttributes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
|
||||
* A new {@link ErrorController}.
|
||||
*
|
||||
* @param errorAttributes the error attributes
|
||||
* @param serverProperties configuration properties
|
||||
* @param webProperties web configuration properties
|
||||
*/
|
||||
public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) {
|
||||
super(errorAttributes, serverProperties.getError());
|
||||
public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
|
||||
super(errorAttributes, webProperties.getError());
|
||||
}
|
||||
|
||||
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.app.mgmt;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@@ -59,11 +60,13 @@ class CorsTest extends AbstractSecurityTest {
|
||||
.getContentAsString();
|
||||
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
|
||||
|
||||
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(
|
||||
"/some_uri", ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden())
|
||||
performOptionsRequestToUrlWithOrigin("/some_uri", ALLOWED_ORIGIN_FIRST)
|
||||
// TODO (Spring Boot 4 Migration): Since 4.0 at lest test returns 200 for not found - not going via CORS
|
||||
// before it was 403 + INVALID_CORS_REQUEST
|
||||
// .andExpect(status().isForbidden()).andExpect(content().string(INVALID_CORS_REQUEST)) // Spring Boot 3
|
||||
.andExpect(content().string(""))
|
||||
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
|
||||
.getResponse().getContentAsString();
|
||||
assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
|
||||
}
|
||||
|
||||
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<!-- Spring - START -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -109,7 +109,7 @@ public class MgmtSecurityConfiguration {
|
||||
// could be used for instance to set authentication provider
|
||||
// Note: implementation of the customizer shall always take in account what is the already set by the hawkBit
|
||||
@Autowired(required = false) @Qualifier("hawkbitHttpSecurityCustomizer") final Customizer<HttpSecurity> httpSecurityCustomizer,
|
||||
final SystemManagement systemManagement) throws Exception {
|
||||
final SystemManagement systemManagement) {
|
||||
http
|
||||
.securityMatcher(MgmtRestConstants.REST + "/**")
|
||||
.authorizeHttpRequests(amrmRegistry -> amrmRegistry
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Database -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
@@ -58,9 +63,14 @@
|
||||
<!-- Database END -->
|
||||
|
||||
<!-- Test -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.security</groupId>-->
|
||||
<!-- <artifactId>spring-security-test</artifactId>-->
|
||||
<!-- <scope>test</scope>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -14,9 +14,9 @@ import java.util.Map;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.autoconfigure.web.WebProperties;
|
||||
import org.springframework.boot.webmvc.autoconfigure.error.BasicErrorController;
|
||||
import org.springframework.boot.webmvc.error.ErrorAttributes;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -38,10 +38,10 @@ public class ErrorController extends BasicErrorController {
|
||||
* A new {@link ErrorController}.
|
||||
*
|
||||
* @param errorAttributes the error attributes
|
||||
* @param serverProperties configuration properties
|
||||
* @param webProperties web configuration properties
|
||||
*/
|
||||
public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) {
|
||||
super(errorAttributes, serverProperties.getError());
|
||||
public ErrorController(final ErrorAttributes errorAttributes, final WebProperties webProperties) {
|
||||
super(errorAttributes, webProperties.getError());
|
||||
}
|
||||
|
||||
@RequestMapping(produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.app;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@@ -59,11 +60,13 @@ class CorsTest extends AbstractSecurityTest {
|
||||
.getContentAsString();
|
||||
assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST);
|
||||
|
||||
final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(
|
||||
"/some_uri", ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden())
|
||||
performOptionsRequestToUrlWithOrigin("/some_uri", ALLOWED_ORIGIN_FIRST)
|
||||
// TODO (Spring Boot 4 Migration): Since 4.0 at lest test returns 200 for not found - not going via CORS
|
||||
// before it was 403 + INVALID_CORS_REQUEST
|
||||
// .andExpect(status().isForbidden()).andExpect(content().string(INVALID_CORS_REQUEST)) // Spring Boot 3
|
||||
.andExpect(content().string(""))
|
||||
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn()
|
||||
.getResponse().getContentAsString();
|
||||
assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST);
|
||||
}
|
||||
|
||||
private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception {
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
<name>hawkBit :: Query Language :: JPA</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
@@ -34,19 +39,9 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -56,6 +51,11 @@
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
|
||||
@@ -22,8 +22,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.query.sqm.internal.QuerySqmImpl;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.internal.SqmQueryImpl;
|
||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
import org.hibernate.query.sqm.sql.SqmTranslation;
|
||||
@@ -33,6 +35,7 @@ import org.hibernate.query.sqm.tree.SqmDmlStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
@@ -60,14 +63,14 @@ public class HibernateUtils {
|
||||
throw new UnsupportedOperationException("SqmTranslatorFactory resolver is not available");
|
||||
}
|
||||
|
||||
final QuerySqmImpl<?> hqlQuery = query.unwrap(QuerySqmImpl.class);
|
||||
final SessionFactoryImplementor factory = hqlQuery.getSessionFactory();
|
||||
final SqmQueryImpl<?> hqlQuery = query.unwrap(SqmQueryImpl.class);
|
||||
final SessionFactoryImplementor sessionFactory = hqlQuery.getSessionFactory();
|
||||
final SharedSessionContractImplementor session = hqlQuery.getSession();
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
final SqlAstCreationContext sqlAstCreationContext = (SqlAstCreationContext)session.getFactory();
|
||||
|
||||
final SqmTranslatorFactory sqmTranslatorFactory;
|
||||
try {
|
||||
sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(factory.getQueryEngine());
|
||||
sqmTranslatorFactory = (SqmTranslatorFactory) getSqmTranslatorFactory.invoke(sessionFactory.getQueryEngine());
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
throw new UnsupportedOperationException("Can't create SqmTranslatorFactory", e);
|
||||
}
|
||||
@@ -76,19 +79,20 @@ public class HibernateUtils {
|
||||
hqlQuery.getSqmStatement() instanceof SqmSelectStatement<?> selectStatement
|
||||
? sqmTranslatorFactory.createSelectTranslator(selectStatement,
|
||||
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
|
||||
hqlQuery.getLoadQueryInfluencers(), sessionFactory, false)
|
||||
hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext, false)
|
||||
: sqmTranslatorFactory.createMutationTranslator((SqmDmlStatement<?>) hqlQuery.getSqmStatement(),
|
||||
hqlQuery.getQueryOptions(), hqlQuery.getDomainParameterXref(), hqlQuery.getQueryParameterBindings(),
|
||||
hqlQuery.getLoadQueryInfluencers(), sessionFactory);
|
||||
hqlQuery.getLoadQueryInfluencers(), sqlAstCreationContext);
|
||||
|
||||
final SqmTranslation<? extends Statement> sqmTranslation = sqmSelectTranslator.translate();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = factory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory();
|
||||
final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
|
||||
hqlQuery.getDomainParameterXref(), sqmTranslation::getJdbcParamsBySqmParam);
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(hqlQuery.getQueryParameterBindings(),
|
||||
hqlQuery.getDomainParameterXref(), jdbcParamsXref, factory.getRuntimeMetamodels().getMappingMetamodel(),
|
||||
sqmSelectTranslator.getFromClauseAccess()::findTableGroup, new SqmParameterMappingModelResolutionAccess() {
|
||||
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
|
||||
(QueryParameterBindings) hqlQuery.getQueryParameterBindings(),
|
||||
(DomainParameterXref) hqlQuery.getDomainParameterXref(), jdbcParamsXref, /*sessionFactory.getRuntimeMetamodels().getMappingMetamodel(),
|
||||
sqmSelectTranslator.getFromClauseAccess()::findTableGroup,*/ new SqmParameterMappingModelResolutionAccess() {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -97,9 +101,9 @@ public class HibernateUtils {
|
||||
}
|
||||
}, hqlQuery.getSession());
|
||||
return (sqmTranslation.getSqlAst() instanceof SelectStatement selectStatement
|
||||
? sqlAstTranslatorFactory.buildSelectTranslator(factory, selectStatement)
|
||||
? sqlAstTranslatorFactory.buildSelectTranslator(sessionFactory, selectStatement)
|
||||
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions())
|
||||
: sqlAstTranslatorFactory.buildMutationTranslator(factory, (MutationStatement) sqmTranslation.getSqlAst())
|
||||
: sqlAstTranslatorFactory.buildMutationTranslator(sessionFactory, (MutationStatement) sqmTranslation.getSqlAst())
|
||||
.translate(jdbcParameterBindings, hqlQuery.getQueryOptions()))
|
||||
.getSqlString();
|
||||
}
|
||||
|
||||
@@ -26,12 +26,11 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
@SuppressWarnings("java:S5961") // complex check because the matter is very complex
|
||||
@DataJpaTest(properties = { "spring.jpa.database=H2" }, excludeAutoConfiguration = { FlywayAutoConfiguration.class })
|
||||
@DataJpaTest(properties = "spring.jpa.database=H2")
|
||||
@EnableAutoConfiguration
|
||||
@Slf4j
|
||||
class SpecificationBuilderTest {
|
||||
|
||||
@@ -34,9 +34,10 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.cronutils</groupId>
|
||||
<artifactId>cron-utils</artifactId>
|
||||
|
||||
@@ -49,7 +49,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ public abstract class AbstractRemoteEvent extends ApplicationEvent {
|
||||
@Serial
|
||||
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
|
||||
protected AbstractRemoteEvent() {
|
||||
|
||||
@@ -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
|
||||
* to be really include in the cache eviction process the subclasses must declare that it implements that interface.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@@ -34,13 +34,13 @@ public abstract class RemoteIdEvent extends RemoteTenantAwareEvent {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long entityId;
|
||||
private String entityClass;
|
||||
private final Long entityId;
|
||||
private final String entityClass;
|
||||
|
||||
protected RemoteIdEvent(final String tenant, final Long entityId, final Class<? extends TenantAwareBaseEntity> entityClass) {
|
||||
super(tenant, entityId);
|
||||
this.entityClass = entityClass.getName();
|
||||
this.entityId = entityId;
|
||||
this.entityClass = entityClass.getName();
|
||||
}
|
||||
|
||||
public String getCacheName() {
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
|
||||
* distributed events. All the necessary information of distributing events to
|
||||
* other nodes.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED) // for serialization libs like jackson
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) // for serialization libs like jackson
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@@ -33,7 +33,7 @@ public class RemoteTenantAwareEvent extends AbstractRemoteEvent implements Tenan
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String tenant;
|
||||
private final String tenant;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
||||
@@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Defines the remote event of creating a new {@link Action}.
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -24,6 +25,7 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre
|
||||
@Serial
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
@JsonIgnore
|
||||
public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
|
||||
super(action, targetId, rolloutId, rolloutGroupId);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -24,6 +25,7 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd
|
||||
@Serial
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
@JsonIgnore
|
||||
public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId) {
|
||||
super(action, targetId, rolloutId, rolloutGroupId);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -24,6 +25,7 @@ public class DistributionSetCreatedEvent extends RemoteEntityEvent<DistributionS
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetCreatedEvent(final DistributionSet distributionSet) {
|
||||
super(distributionSet);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
@@ -24,6 +25,7 @@ public class DistributionSetTagCreatedEvent extends RemoteEntityEvent<Distributi
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetTagCreatedEvent(final DistributionSetTag distributionSetTag) {
|
||||
super(distributionSetTag);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
@@ -24,6 +25,7 @@ public class DistributionSetTagUpdatedEvent extends RemoteEntityEvent<Distributi
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetTagUpdatedEvent(final DistributionSetTag distributionSetTag) {
|
||||
super(distributionSetTag);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
@@ -25,6 +26,7 @@ public class DistributionSetTypeCreatedEvent extends RemoteEntityEvent<Distribut
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetTypeCreatedEvent(final DistributionSetType distributionSetType) {
|
||||
super(distributionSetType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
@@ -26,6 +27,7 @@ public class DistributionSetTypeUpdatedEvent extends RemoteEntityEvent<Distribut
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetTypeUpdatedEvent(final DistributionSetType distributionSetType) {
|
||||
super(distributionSetType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -28,6 +29,7 @@ public class DistributionSetUpdatedEvent extends RemoteEntityEvent<DistributionS
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public DistributionSetUpdatedEvent(final DistributionSet distributionSet) {
|
||||
super(distributionSet);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
@@ -24,6 +25,7 @@ public class RolloutCreatedEvent extends RemoteEntityEvent<Rollout> implements E
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public RolloutCreatedEvent(final Rollout rollout) {
|
||||
super(rollout);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
@@ -25,6 +26,7 @@ public class RolloutGroupCreatedEvent extends AbstractRolloutGroupEvent implemen
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public RolloutGroupCreatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
|
||||
super(rolloutGroup, rolloutId);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
@@ -24,6 +25,7 @@ public class RolloutGroupUpdatedEvent extends AbstractRolloutGroupEvent implemen
|
||||
@Serial
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
@JsonIgnore
|
||||
public RolloutGroupUpdatedEvent(final RolloutGroup rolloutGroup, final Long rolloutId) {
|
||||
super(rolloutGroup, rolloutId);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
@@ -24,6 +25,7 @@ public class RolloutUpdatedEvent extends RemoteEntityEvent<Rollout> implements E
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public RolloutUpdatedEvent(final Rollout rollout) {
|
||||
super(rollout);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
@@ -24,6 +25,7 @@ public class SoftwareModuleCreatedEvent extends RemoteEntityEvent<SoftwareModule
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public SoftwareModuleCreatedEvent(final SoftwareModule softwareModule) {
|
||||
super(softwareModule);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
@@ -24,6 +25,7 @@ public class SoftwareModuleTypeCreatedEvent extends RemoteEntityEvent<SoftwareMo
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public SoftwareModuleTypeCreatedEvent(final SoftwareModuleType softwareModuleType) {
|
||||
super(softwareModuleType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
@@ -25,6 +26,7 @@ public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent<SoftwareMo
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public SoftwareModuleTypeUpdatedEvent(final SoftwareModuleType softwareModuleType) {
|
||||
super(softwareModuleType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
@@ -25,6 +26,7 @@ public class SoftwareModuleUpdatedEvent extends RemoteEntityEvent<SoftwareModule
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public SoftwareModuleUpdatedEvent(final SoftwareModule softwareModule) {
|
||||
super(softwareModule);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -24,6 +25,7 @@ public class TargetCreatedEvent extends RemoteEntityEvent<Target> implements Ent
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetCreatedEvent(final Target target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
@@ -24,6 +25,7 @@ public class TargetFilterQueryCreatedEvent extends RemoteEntityEvent<TargetFilte
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetFilterQueryCreatedEvent(final TargetFilterQuery targetFilterQuery) {
|
||||
super(targetFilterQuery);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
@@ -24,6 +25,7 @@ public class TargetFilterQueryUpdatedEvent extends RemoteEntityEvent<TargetFilte
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetFilterQueryUpdatedEvent(final TargetFilterQuery targetFilterQuery) {
|
||||
super(targetFilterQuery);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
@@ -24,6 +25,7 @@ public class TargetTagCreatedEvent extends RemoteEntityEvent<TargetTag> implemen
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetTagCreatedEvent(final TargetTag targetTag) {
|
||||
super(targetTag);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
@@ -24,6 +25,7 @@ public class TargetTagUpdatedEvent extends RemoteEntityEvent<TargetTag> implemen
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetTagUpdatedEvent(final TargetTag targetTag) {
|
||||
super(targetTag);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
@@ -25,6 +26,7 @@ public class TargetTypeCreatedEvent extends RemoteEntityEvent<TargetType> implem
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetTypeCreatedEvent(final TargetType targetType) {
|
||||
super(targetType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
@@ -25,6 +26,7 @@ public class TargetTypeUpdatedEvent extends RemoteEntityEvent<TargetType> implem
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetTypeUpdatedEvent(final TargetType targetType) {
|
||||
super(targetType);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -24,6 +25,7 @@ public class TargetUpdatedEvent extends RemoteEntityEvent<Target> implements Ent
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TargetUpdatedEvent(final Target target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
@@ -24,6 +25,7 @@ public class TenantConfigurationCreatedEvent extends RemoteEntityEvent<TenantCon
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TenantConfigurationCreatedEvent(final TenantConfiguration tenantConfiguration) {
|
||||
super(tenantConfiguration);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
@@ -25,6 +26,7 @@ public class TenantConfigurationUpdatedEvent extends RemoteEntityEvent<TenantCon
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@JsonIgnore
|
||||
public TenantConfigurationUpdatedEvent(final TenantConfiguration tenantConfiguration) {
|
||||
super(tenantConfiguration);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,10 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.protostuff</groupId>
|
||||
<artifactId>protostuff-core</artifactId>
|
||||
@@ -42,9 +43,5 @@
|
||||
<artifactId>protostuff-runtime</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -9,30 +9,35 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
|
||||
import org.springframework.messaging.converter.JacksonJsonMessageConverter;
|
||||
import org.springframework.util.MimeType;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
public class EventJacksonMessageConverter extends MappingJackson2MessageConverter {
|
||||
public class EventJacksonMessageConverter extends JacksonJsonMessageConverter {
|
||||
|
||||
public static final MimeType APPLICATION_REMOTE_EVENT_JSON = new MimeType("application", "remote-event-json");
|
||||
|
||||
public EventJacksonMessageConverter() {
|
||||
super(APPLICATION_REMOTE_EVENT_JSON);
|
||||
final ObjectMapper objectMapper = new ObjectMapper();
|
||||
EventType.getNamedTypes().forEach(objectMapper::registerSubtypes);
|
||||
setObjectMapper(objectMapper);
|
||||
public EventJacksonMessageConverter(final JsonMapper mapper) {
|
||||
super(mapper, APPLICATION_REMOTE_EVENT_JSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object convertToInternal(final Object payload, final MessageHeaders headers, final Object conversionHint) {
|
||||
@SuppressWarnings("java:S1185") // intentionally override in order to extend visibility
|
||||
@NullMarked
|
||||
@Nullable
|
||||
protected Object convertToInternal(final Object payload, @Nullable final MessageHeaders headers, @Nullable final Object conversionHint) {
|
||||
return super.convertToInternal(payload, headers, conversionHint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object convertFromInternal(final Message<?> message, final Class<?> targetClass, final Object conversionHint) {
|
||||
@SuppressWarnings("java:S1185") // intentionally override in order to extend visibility
|
||||
@NullMarked
|
||||
@Nullable
|
||||
protected Object convertFromInternal(final Message<?> message, final Class<?> targetClass, @Nullable final Object conversionHint) {
|
||||
return super.convertFromInternal(message, targetClass, conversionHint);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import io.protostuff.Schema;
|
||||
import io.protostuff.runtime.RuntimeSchema;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.AbstractMessageConverter;
|
||||
@@ -46,18 +48,20 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
|
||||
public static final MimeType APPLICATION_BINARY_PROTOSTUFF = new MimeType("application", "binary+protostuff");
|
||||
private static final int HEADER_LENGTH_PREFIX_SIZE = 4;
|
||||
|
||||
|
||||
public EventProtoStuffMessageConverter() {
|
||||
super(APPLICATION_BINARY_PROTOSTUFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NullMarked
|
||||
protected boolean supports(final Class<?> aClass) {
|
||||
return AbstractRemoteEvent.class.isAssignableFrom(aClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object convertFromInternal(final Message<?> message, final Class<?> targetClass, final Object conversionHint) {
|
||||
@NullMarked
|
||||
@Nullable
|
||||
protected Object convertFromInternal(final Message<?> message, final Class<?> targetClass, @Nullable final Object conversionHint) {
|
||||
final Object objectPayload = message.getPayload();
|
||||
if (objectPayload instanceof byte[] payload) {
|
||||
final byte[] clazzHeader = extractClazzHeader(payload);
|
||||
@@ -112,7 +116,6 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
private static EventType readClassHeader(final byte[] typeInformation) {
|
||||
final Schema<EventType> schema = RuntimeSchema.getSchema(EventType.class);
|
||||
final EventType deserializedType = schema.newMessage();
|
||||
@@ -134,8 +137,7 @@ public class EventProtoStuffMessageConverter extends AbstractMessageConverter {
|
||||
throw new MessageConversionException("Missing EventType for given class : " + clazz);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final Schema<Object> schema = (Schema<Object>) RuntimeSchema.getSchema((Class<?>) EventType.class);
|
||||
@SuppressWarnings("unchecked") final Schema<Object> schema = (Schema<Object>) RuntimeSchema.getSchema((Class<?>) EventType.class);
|
||||
final LinkedBuffer buffer = LinkedBuffer.allocate();
|
||||
byte[] typeBytes = ProtobufIOUtil.toByteArray(clazzEventType, schema, buffer);
|
||||
|
||||
|
||||
@@ -29,17 +29,20 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.ApplicationEventMulticaster;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
/**
|
||||
* Autoconfiguration for the events.
|
||||
* Autoconfiguration for the events publishing.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Import(EventJacksonConfiguration.class)
|
||||
public class EventPublisherConfiguration {
|
||||
|
||||
/**
|
||||
@@ -108,15 +111,21 @@ public class EventPublisherConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Consumer<AbstractRemoteEvent> serviceEventConsumer(ApplicationEventPublisher publisher) {
|
||||
public Consumer<AbstractRemoteEvent> serviceEventConsumer(final ApplicationEventPublisher publisher) {
|
||||
return publisher::publishEvent;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Consumer<AbstractRemoteEvent> fanoutEventConsumer(ApplicationEventPublisher publisher) {
|
||||
public Consumer<AbstractRemoteEvent> fanoutEventConsumer(final ApplicationEventPublisher publisher) {
|
||||
return publisher::publishEvent;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageConverter eventJacksonMessageConverter(final JsonMapper mapper) {
|
||||
return new EventJacksonMessageConverter(mapper);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Schema.class, ProtostuffIOUtil.class })
|
||||
protected static class EventProtostuffConfiguration {
|
||||
|
||||
@@ -125,9 +134,4 @@ public class EventPublisherConfiguration {
|
||||
return new EventProtoStuffMessageConverter();
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageConverter eventJacksonMessageConverter() {
|
||||
return new EventJacksonMessageConverter();
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
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.TargetPollServiceEvent;
|
||||
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
|
||||
* encoding value in the payload of an remote header. The event-type is encoded
|
||||
* into the payload of the message which is distributed.
|
||||
*
|
||||
* To encode and decode the event class type we need some conversation mapping
|
||||
* between the actual class and the corresponding integer value which is the
|
||||
* encoded value in the byte-payload.
|
||||
* The {@link EventType} class declares the event-type, and it's corresponding encoding value in the payload of a remote header.
|
||||
* The event-type is encoded 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
|
||||
* which is the encoded value in the byte-payload.
|
||||
*/
|
||||
// for marshalling and unmarshalling.
|
||||
@NoArgsConstructor
|
||||
@@ -93,8 +90,7 @@ public class EventType {
|
||||
|
||||
private int value;
|
||||
|
||||
// The associated event-type-value must remain the same as initially
|
||||
// declared. Otherwise, messages cannot correctly de-serialized.
|
||||
// The associated event-type-value must remain the same as initially declared. Otherwise, messages cannot correctly de-serialized.
|
||||
static {
|
||||
// target
|
||||
TYPES.put(1, TargetCreatedEvent.class);
|
||||
@@ -215,9 +211,9 @@ public class EventType {
|
||||
return TYPES.get(value);
|
||||
}
|
||||
|
||||
public static Collection<NamedType> getNamedTypes() {
|
||||
public static NamedType[] getNamedTypes() {
|
||||
return TYPES.entrySet().stream()
|
||||
.map(e -> new NamedType(e.getValue(), String.valueOf(e.getKey())))
|
||||
.toList();
|
||||
.toArray(NamedType[]::new);
|
||||
}
|
||||
}
|
||||
@@ -35,20 +35,14 @@ import org.springframework.messaging.converter.MessageConverter;
|
||||
|
||||
abstract class AbstractEventMessageConverterTest {
|
||||
|
||||
protected final MessageConverter messageConverter;
|
||||
|
||||
@Mock
|
||||
protected Message<Object> messageMock;
|
||||
|
||||
@Mock
|
||||
protected Target targetMock;
|
||||
|
||||
@Mock
|
||||
protected Action actionMock;
|
||||
|
||||
AbstractEventMessageConverterTest(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
protected abstract MessageConverter messageConverter();
|
||||
|
||||
/**
|
||||
* Verifies that the TargetCreatedEvent can be successfully serialized and deserialized
|
||||
@@ -82,11 +76,8 @@ abstract class AbstractEventMessageConverterTest {
|
||||
|
||||
@Test
|
||||
void successfullySerializeAndDeserializeActionServiceEvent() {
|
||||
final ActionCreatedServiceEvent actionCreatedServiceEvent =
|
||||
new ActionCreatedServiceEvent(createActionCreatedEvent());
|
||||
|
||||
final ActionUpdatedServiceEvent actionUpdatedServiceEvent =
|
||||
new ActionUpdatedServiceEvent(createActionUpdatedEvent());
|
||||
final ActionCreatedServiceEvent actionCreatedServiceEvent = new ActionCreatedServiceEvent(createActionCreatedEvent());
|
||||
final ActionUpdatedServiceEvent actionUpdatedServiceEvent = new ActionUpdatedServiceEvent(createActionUpdatedEvent());
|
||||
|
||||
assertSerializeAndDeserialize(actionCreatedServiceEvent, ActionCreatedServiceEvent.class);
|
||||
assertSerializeAndDeserialize(actionUpdatedServiceEvent, ActionUpdatedServiceEvent.class);
|
||||
@@ -112,7 +103,9 @@ abstract class AbstractEventMessageConverterTest {
|
||||
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
|
||||
Object serializedEvent = null;
|
||||
if (messageConverter instanceof EventProtoStuffMessageConverter protoStuff) {
|
||||
@@ -135,4 +128,4 @@ abstract class AbstractEventMessageConverterTest {
|
||||
.isInstanceOf(expectedClass)
|
||||
.isEqualTo(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,27 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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)
|
||||
@SpringBootTest(classes = EventJacksonConfiguration.class)
|
||||
class EventJacksonMessageConverterTest extends AbstractEventMessageConverterTest {
|
||||
|
||||
EventJacksonMessageConverterTest() {
|
||||
super(new EventJacksonMessageConverter());
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
@Autowired
|
||||
void setJsonMapper(final JsonMapper jsonMapper) {
|
||||
messageConverter = new EventJacksonMessageConverter(jsonMapper);
|
||||
}
|
||||
|
||||
protected MessageConverter messageConverter() {
|
||||
return Objects.requireNonNull(messageConverter, "MessageConverter has not been initialized");
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,15 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConversionException;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class EventProtoStuffMessageConverterTest extends AbstractEventMessageConverterTest {
|
||||
|
||||
EventProtoStuffMessageConverterTest() {
|
||||
super(new EventProtoStuffMessageConverter());
|
||||
private static final EventProtoStuffMessageConverter MESSAGE_CONVERTER = new EventProtoStuffMessageConverter();
|
||||
|
||||
protected MessageConverter messageConverter() {
|
||||
return MESSAGE_CONVERTER;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +42,7 @@ class EventProtoStuffMessageConverterTest extends AbstractEventMessageConverterT
|
||||
|
||||
assertThatExceptionOfType(MessageConversionException.class)
|
||||
.as("Missing MessageConversationException for un-defined event-type")
|
||||
.isThrownBy(() -> ((EventProtoStuffMessageConverter)messageConverter).convertToInternal(dummyEvent, messageHeaders, null));
|
||||
.isThrownBy(() -> MESSAGE_CONVERTER.convertToInternal(dummyEvent, messageHeaders, null));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,12 +41,6 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -33,24 +33,23 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Static class generation -->
|
||||
<dependency>
|
||||
<dependency> <!-- Static class generation -->
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<artifactId>hibernate-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -71,13 +70,6 @@
|
||||
<configuration>
|
||||
<basePackage>org.eclipse.hawkbit.repository.jpa.model</basePackage>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -17,7 +17,7 @@ import jakarta.persistence.PersistenceException;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ import javax.sql.DataSource;
|
||||
import lombok.Data;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaBaseConfiguration;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
import org.springframework.boot.transaction.autoconfigure.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -38,7 +38,7 @@ import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties // predix is "/" intentionally
|
||||
@ConfigurationProperties // prefix is "/" intentionally
|
||||
protected static class Properties {
|
||||
|
||||
private final Map<String, String> eclipselink = new HashMap<>();
|
||||
|
||||
@@ -42,9 +42,9 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||
* <ol>
|
||||
* <li>The Spring property spring.jpa.properties.eclipselink.profiler=PerformanceMonitor shall be set - enables Eclipselink statistics
|
||||
* 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>
|
||||
* <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>
|
||||
* </ol>
|
||||
*
|
||||
|
||||
@@ -24,16 +24,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<artifactId>spring-boot-starter-flyway</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -10,7 +10,7 @@
|
||||
package org.eclipse.hawkbit.autoconfigure.repository.jpa.flyway;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER TIMESTAMP NOT NULL;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE SP_LOCK ADD COLUMN EXPIRED_AFTER DATETIME(6) NOT NULL;
|
||||
@@ -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
Reference in New Issue
Block a user