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

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

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

View File

@@ -27,26 +27,5 @@
<artifactId>hawkbit-core</artifactId>
<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>

View File

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

View File

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

View File

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