From 10f1f9fede99c11876dcd32e6cbe98ce7a23728e Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Wed, 5 Aug 2026 14:26:50 +0200 Subject: [PATCH 1/6] update dependencies --- .github/workflows/main.yml | 4 +-- .github/workflows/release.yml | 4 +-- .idea/misc.xml | 4 +-- pom.xml | 34 ++++++++----------- readme.md | 2 +- .../lib/application/ManageEmailTest.java | 8 ++--- .../lib/application/QueryEmailTest.java | 4 +-- .../database/PostgresTestcontainer.java | 2 +- .../database/factory/EmailFactory.java | 2 +- 9 files changed, 29 insertions(+), 35 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 96ef44f..3b8ddf9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - uses: aboutbits/github-actions-java/setup-with-maven@v4 with: - java-version: 21 + java-version: 26 - name: Test env: GITHUB_USER_NAME: ${{ github.actor }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2b0643..5107bd7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 with: token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} - uses: aboutbits/github-actions-base/git-setup@v2 - uses: aboutbits/github-actions-java/setup-with-maven@v4 with: - java-version: 21 + java-version: 26 - uses: aboutbits/github-actions-java/set-version-with-maven@v4 with: version: "${{ github.event.inputs.version }}" diff --git a/.idea/misc.xml b/.idea/misc.xml index 68a40a6..c725a8d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,8 +8,8 @@ - + - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 6de6955..e5e42b5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.2 + 4.0.4 @@ -16,8 +16,8 @@ Spring Boot Email Service - 21 - 1.5.5.Final + 26 + 1.6.3 @@ -57,39 +57,33 @@ org.postgresql postgresql - - io.hypersistence - hypersistence-utils-hibernate-63 - 3.7.0 - com.fasterxml.jackson.core jackson-databind - - com.github.javafaker - javafaker - 1.0.2 + net.datafaker + datafaker + 2.7.0 test org.testcontainers testcontainers - 1.18.0 + 2.0.5 test org.testcontainers - junit-jupiter - 1.18.0 + testcontainers-junit-jupiter + 2.0.5 test org.testcontainers - postgresql - 1.18.0 + testcontainers-postgresql + 2.0.5 test @@ -99,7 +93,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.15.0 ${java.version} ${java.version} @@ -129,7 +123,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.1 + 3.6.0 checkstyle.xml @@ -154,7 +148,7 @@ com.puppycrawl.tools checkstyle - 10.3.4 + 13.5.0 diff --git a/readme.md b/readme.md index f0aa6b3..19c5434 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ A reusable mailer service to send emails. ## Setup -Add the mailer service to the classpath by adding the following maven dependency. Versions haven be found [here](../../packages) +Add the mailer service to the classpath by adding the following maven dependency. Versions can be found [here](../../packages) ```xml diff --git a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java index e91b1e6..3a5e1a4 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.mail.MailSendException; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.io.ByteArrayInputStream; import java.time.OffsetDateTime; @@ -31,10 +31,10 @@ @SpringBootTest @WithPostgres class ManageEmailTest { - @SpyBean + @MockitoSpyBean JavaMailSender javaMailSender; - @MockBean + @MockitoBean AttachmentDataSource attachmentDataSource; @Autowired diff --git a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java index ab7e5e4..a99be68 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java @@ -8,9 +8,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.PageRequest; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import java.util.Set; @@ -20,7 +20,7 @@ @SpringBootTest @WithPostgres class QueryEmailTest { - @MockBean + @MockitoBean JavaMailSender javaMailSender; @Autowired diff --git a/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java b/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java index 90bc89e..cb5c2ae 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java @@ -101,7 +101,7 @@ private void cleanTablesData(List tablesToClean, Connection connectio } @Data - private static class TableData { + private static final class TableData { private final String schema; private final String name; diff --git a/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java b/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java index ef841d8..6d7af82 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java @@ -1,8 +1,8 @@ package it.aboutbits.springboot.emailservice.support.database.factory; -import com.github.javafaker.Faker; import it.aboutbits.springboot.emailservice.lib.EmailState; import it.aboutbits.springboot.emailservice.lib.model.Email; +import net.datafaker.Faker; import java.time.OffsetDateTime; import java.util.List; From 7bcee972020f7ff046142c32ef8625c90d1e9183 Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Wed, 5 Aug 2026 14:45:07 +0200 Subject: [PATCH 2/6] revert to java 25 and fix deprecated annotations --- .github/workflows/main.yml | 2 +- .github/workflows/release.yml | 2 +- .idea/misc.xml | 2 +- pom.xml | 2 +- .../springboot/emailservice/lib/EmailDto.java | 2 +- .../lib/application/EmailParameter.java | 17 +++-------------- .../lib/application/ManageEmail.java | 7 +++---- .../lib/application/package-info.java | 6 ++---- .../emailservice/lib/jpa/EmailRepository.java | 2 +- .../emailservice/lib/model/Email.java | 2 +- .../emailservice/lib/package-info.java | 6 ++---- 11 files changed, 17 insertions(+), 33 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b8ddf9..30d17ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v7 - uses: aboutbits/github-actions-java/setup-with-maven@v4 with: - java-version: 26 + java-version: 25 - name: Test env: GITHUB_USER_NAME: ${{ github.actor }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5107bd7..935be73 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: - uses: aboutbits/github-actions-base/git-setup@v2 - uses: aboutbits/github-actions-java/setup-with-maven@v4 with: - java-version: 26 + java-version: 25 - uses: aboutbits/github-actions-java/set-version-with-maven@v4 with: version: "${{ github.event.inputs.version }}" diff --git a/.idea/misc.xml b/.idea/misc.xml index c725a8d..cc6aff1 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + diff --git a/pom.xml b/pom.xml index e5e42b5..78707ab 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ Spring Boot Email Service - 26 + 25 1.6.3 diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java index d910922..e81e857 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.emailservice.lib; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import java.time.OffsetDateTime; import java.util.List; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java index 21cd008..ca38b2b 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java @@ -3,9 +3,8 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import lombok.Builder; -import lombok.NonNull; import lombok.Singular; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import java.io.InputStream; import java.time.OffsetDateTime; @@ -14,30 +13,24 @@ @Builder public record EmailParameter( - @NonNull OffsetDateTime scheduledAt, - @NonNull Email email ) { @Builder public record Email( - @NonNull @NotBlank String subject, @Singular - @NonNull @NotEmpty List recipients, - @NonNull String textBody, - @NonNull String htmlBody, + String textBody, + String htmlBody, - @NonNull @NotBlank String fromAddress, - @NonNull @NotBlank String fromName, @@ -47,17 +40,13 @@ public record Email( String replyToName, @Singular - @NonNull Set attachments ) { @Builder public record Attachment( - @NonNull InputStream payload, - @NonNull @NotBlank String fileName, - @NonNull @NotBlank String contentType ) { diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java index 1fe4c24..3d4ea0c 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java @@ -11,10 +11,9 @@ import it.aboutbits.springboot.emailservice.lib.model.EmailAttachment; import jakarta.mail.MessagingException; import jakarta.validation.Valid; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ByteArrayResource; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -48,7 +47,7 @@ public ManageEmail( this.emailMapper = emailMapper; } - public EmailDto schedule(@NonNull @Valid EmailParameter parameter) throws EmailException { + public EmailDto schedule(@Valid EmailParameter parameter) throws EmailException { Email email; try { email = fromParameter(parameter); @@ -61,7 +60,7 @@ public EmailDto schedule(@NonNull @Valid EmailParameter parameter) throws EmailE return emailMapper.toDto(savedEmail); } - public EmailDto sendOrFail(@NonNull @Valid EmailParameter parameter) throws EmailException { + public EmailDto sendOrFail(@Valid EmailParameter parameter) throws EmailException { Email email; try { email = fromParameter(parameter); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java index 20c8675..62e090e 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java @@ -1,6 +1,4 @@ -@NonNullApi -@NonNullFields +@NullMarked package it.aboutbits.springboot.emailservice.lib.application; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java index 718edf8..4c9b6fc 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java @@ -3,11 +3,11 @@ import it.aboutbits.springboot.emailservice.lib.EmailState; import it.aboutbits.springboot.emailservice.lib.model.Email; +import org.jspecify.annotations.NonNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.lang.NonNull; import java.util.Collection; import java.util.List; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java index 20ea001..19cf34d 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java @@ -21,7 +21,7 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.UpdateTimestamp; import org.hibernate.type.SqlTypes; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import java.time.OffsetDateTime; import java.util.List; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java index 48744f3..5e3a1e3 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java @@ -1,6 +1,4 @@ -@NonNullApi -@NonNullFields +@NullMarked package it.aboutbits.springboot.emailservice.lib; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; From 066ce65c6e730a56adc786e2c770525f6ed2ccd6 Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Thu, 6 Aug 2026 12:16:40 +0200 Subject: [PATCH 3/6] add nullaway, errorprone, archunit and remove package-info-files --- pom.xml | 49 +++++++++++++++++++ .../EmailServiceConfiguration.java | 2 + .../lib/AttachmentCleanerCallback.java | 3 ++ .../lib/AttachmentDataSource.java | 2 + .../emailservice/lib/EmailAttachmentDto.java | 2 + .../springboot/emailservice/lib/EmailDto.java | 5 ++ .../lib/EmailSchedulerCallback.java | 3 ++ .../application/CleanupAttachmentFiles.java | 2 + .../application/EmailAttachmentMapper.java | 5 ++ .../lib/application/EmailMapper.java | 5 ++ .../lib/application/EmailParameter.java | 3 +- .../lib/application/EmailServiceMigrator.java | 2 + .../lib/application/ManageEmail.java | 5 +- .../lib/application/QueryEmail.java | 30 ++++++++---- .../lib/application/SendScheduledEmails.java | 2 + .../UnavailableAttachmentDataSource.java | 2 + .../lib/application/package-info.java | 4 -- .../lib/exception/AttachmentException.java | 10 +++- .../lib/exception/EmailException.java | 3 ++ .../emailservice/lib/jpa/EmailRepository.java | 10 ++-- .../emailservice/lib/model/Email.java | 5 +- .../lib/model/EmailAttachment.java | 2 + .../emailservice/lib/package-info.java | 4 -- 23 files changed, 129 insertions(+), 31 deletions(-) delete mode 100644 src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java delete mode 100644 src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java diff --git a/pom.xml b/pom.xml index 78707ab..3ccea57 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,20 @@ 25 1.6.3 + 0.13.2 + 2.49.0 + + + + it.aboutbits + archunit-toolbox + 1.1.0 + + + + org.springframework.boot @@ -86,6 +98,17 @@ 2.0.5 test + + com.tngtech.archunit + archunit-junit5 + 1.4.2 + test + + + it.aboutbits + archunit-toolbox + test + @@ -113,11 +136,37 @@ lombok-mapstruct-binding 0.2.0 + + com.google.errorprone + error_prone_core + ${errorprone.version} + + + com.uber.nullaway + nullaway + ${nullaway.version} + -Amapstruct.defaultComponentModel=spring -Amapstruct.unmappedTargetPolicy=ERROR + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + + -Xplugin:ErrorProne -XepDisableAllChecks -XepOpt:NullAway:AnnotatedPackages=it.aboutbits.springboot.emailservice -XepOpt:NullAway:JSpecifyMode=true -Xep:NullAway:ERROR -XepOpt:NullAway:ExcludedFieldAnnotations=org.mockito.InjectMocks,org.mockito.Mock + + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + true diff --git a/src/main/java/it/aboutbits/springboot/emailservice/EmailServiceConfiguration.java b/src/main/java/it/aboutbits/springboot/emailservice/EmailServiceConfiguration.java index ae58265..535054e 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/EmailServiceConfiguration.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/EmailServiceConfiguration.java @@ -15,6 +15,7 @@ import it.aboutbits.springboot.emailservice.lib.application.UnavailableAttachmentDataSource; import it.aboutbits.springboot.emailservice.lib.jpa.EmailRepository; import jakarta.persistence.EntityManager; +import org.jspecify.annotations.NullMarked; import org.springframework.boot.autoconfigure.AutoConfigurationPackage; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -25,6 +26,7 @@ import java.util.List; @AutoConfigurationPackage +@NullMarked public class EmailServiceConfiguration { @Bean(initMethod = "migrate") @ConditionalOnProperty(value = "aboutbits.emailservice.migrations.enabled", matchIfMissing = true) diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentCleanerCallback.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentCleanerCallback.java index 680804e..ef23561 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentCleanerCallback.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentCleanerCallback.java @@ -1,5 +1,8 @@ package it.aboutbits.springboot.emailservice.lib; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface AttachmentCleanerCallback { void report(Report report); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentDataSource.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentDataSource.java index f05b85a..813e1e4 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentDataSource.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/AttachmentDataSource.java @@ -1,9 +1,11 @@ package it.aboutbits.springboot.emailservice.lib; import it.aboutbits.springboot.emailservice.lib.exception.AttachmentException; +import org.jspecify.annotations.NullMarked; import java.io.InputStream; +@NullMarked public interface AttachmentDataSource { InputStream getAttachmentPayload(long fileReference) throws AttachmentException; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailAttachmentDto.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailAttachmentDto.java index 3f138df..dbf6f2d 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailAttachmentDto.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailAttachmentDto.java @@ -1,7 +1,9 @@ package it.aboutbits.springboot.emailservice.lib; import it.aboutbits.springboot.emailservice.lib.model.Email; +import org.jspecify.annotations.NullMarked; +@NullMarked public record EmailAttachmentDto( long id, diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java index e81e857..11ff3ac 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailDto.java @@ -1,11 +1,13 @@ package it.aboutbits.springboot.emailservice.lib; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.time.OffsetDateTime; import java.util.List; import java.util.Set; +@NullMarked public record EmailDto( long id, @@ -29,9 +31,12 @@ public record EmailDto( Set attachments, OffsetDateTime scheduledAt, + @Nullable OffsetDateTime sentAt, + @Nullable OffsetDateTime errorAt, + @Nullable String errorMessage, OffsetDateTime createdAt, diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailSchedulerCallback.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailSchedulerCallback.java index 675b6ca..2db9acd 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailSchedulerCallback.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailSchedulerCallback.java @@ -1,5 +1,8 @@ package it.aboutbits.springboot.emailservice.lib; +import org.jspecify.annotations.NullMarked; + +@NullMarked public interface EmailSchedulerCallback { void report(Report report); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/CleanupAttachmentFiles.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/CleanupAttachmentFiles.java index 7476eb6..df7209d 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/CleanupAttachmentFiles.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/CleanupAttachmentFiles.java @@ -5,6 +5,7 @@ import it.aboutbits.springboot.emailservice.lib.exception.AttachmentException; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.jspecify.annotations.NullMarked; import org.springframework.scheduling.annotation.Scheduled; import java.time.Duration; @@ -12,6 +13,7 @@ @RequiredArgsConstructor @Log4j2 +@NullMarked public class CleanupAttachmentFiles { private static final String JOB_DESCRIPTION = "Cleanup attachments of sent Emails."; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailAttachmentMapper.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailAttachmentMapper.java index e8d572a..8fc8f8b 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailAttachmentMapper.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailAttachmentMapper.java @@ -2,11 +2,16 @@ import it.aboutbits.springboot.emailservice.lib.EmailAttachmentDto; import it.aboutbits.springboot.emailservice.lib.model.EmailAttachment; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.mapstruct.AnnotateWith; import org.mapstruct.Mapper; import java.util.List; @Mapper +@AnnotateWith(NullUnmarked.class) +@NullMarked public interface EmailAttachmentMapper { EmailAttachmentDto toDto(EmailAttachment model); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailMapper.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailMapper.java index c5aa008..48aa8c9 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailMapper.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailMapper.java @@ -2,6 +2,9 @@ import it.aboutbits.springboot.emailservice.lib.EmailDto; import it.aboutbits.springboot.emailservice.lib.model.Email; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.mapstruct.AnnotateWith; import org.mapstruct.Mapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -9,6 +12,8 @@ import java.util.List; @Mapper(uses = EmailAttachmentMapper.class) +@AnnotateWith(NullUnmarked.class) +@NullMarked public interface EmailMapper { EmailDto toDto(Email model); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java index ca38b2b..841032d 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailParameter.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.NotEmpty; import lombok.Builder; import lombok.Singular; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import java.io.InputStream; @@ -12,11 +13,11 @@ import java.util.Set; @Builder +@NullMarked public record EmailParameter( OffsetDateTime scheduledAt, Email email ) { - @Builder public record Email( @NotBlank diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailServiceMigrator.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailServiceMigrator.java index 07d6795..617732a 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailServiceMigrator.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/EmailServiceMigrator.java @@ -1,9 +1,11 @@ package it.aboutbits.springboot.emailservice.lib.application; import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NullMarked; import org.springframework.jdbc.core.JdbcTemplate; @Slf4j +@NullMarked public class EmailServiceMigrator { private final JdbcTemplate jdbcTemplate; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java index 3d4ea0c..809a5f8 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java @@ -12,8 +12,9 @@ import jakarta.mail.MessagingException; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.ByteArrayResource; +import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import org.springframework.core.io.ByteArrayResource; import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -28,6 +29,7 @@ @Validated @Slf4j +@NullMarked public class ManageEmail { private final EmailRepository emailRepository; private final JavaMailSender mailSender; @@ -40,7 +42,6 @@ public ManageEmail( AttachmentDataSource attachmentDataSource, final EmailMapper emailMapper ) { - this.emailRepository = emailRepository; this.mailSender = mailSender; this.attachmentDataSource = attachmentDataSource; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmail.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmail.java index 9714345..7c43588 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmail.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmail.java @@ -7,6 +7,7 @@ import it.aboutbits.springboot.emailservice.lib.model.Email; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; +import org.jspecify.annotations.NullMarked; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; @@ -18,13 +19,18 @@ import java.util.Optional; @RequiredArgsConstructor +@NullMarked public class QueryEmail { private final EmailRepository emailRepository; private final EmailMapper emailMapper; private final EntityManager entityManager; public Page paginatedByState(EmailState state, PageRequest pageParameter) { - var pageRequest = PageRequest.of(pageParameter.getPageNumber(), pageParameter.getPageSize(), Sort.by("updatedAt")); + var pageRequest = PageRequest.of( + pageParameter.getPageNumber(), + pageParameter.getPageSize(), + Sort.by("updatedAt") + ); return emailMapper.toDto(emailRepository.findByState(state, pageRequest)); } @@ -38,12 +44,14 @@ public List byIds(Collection ids) { List readyToSend() { var entityGraph = entityManager.getEntityGraph("email_service_emails-entity-graph"); - return entityManager.createQuery(""" - SELECT e from Email e WHERE e.scheduledAt < :scheduledBefore AND e.state IN ( - it.aboutbits.springboot.emailservice.lib.EmailState.PENDING, - it.aboutbits.springboot.emailservice.lib.EmailState.ERROR - ) - """, Email.class) + return entityManager.createQuery( + """ + SELECT e from Email e WHERE e.scheduledAt < :scheduledBefore AND e.state IN ( + it.aboutbits.springboot.emailservice.lib.EmailState.PENDING, + it.aboutbits.springboot.emailservice.lib.EmailState.ERROR + ) + """, Email.class + ) .setParameter("scheduledBefore", OffsetDateTime.now()) .setHint("jakarta.persistence.fetchgraph", entityGraph) .getResultList(); @@ -51,9 +59,11 @@ List readyToSend() { List readyToCleanup() { var entityGraph = entityManager.getEntityGraph("email_service_emails-entity-graph"); - return entityManager.createQuery(""" - SELECT e from Email e WHERE e.attachmentsCleaned=false AND e.state=it.aboutbits.springboot.emailservice.lib.EmailState.SENT - """, Email.class) + return entityManager.createQuery( + """ + SELECT e from Email e WHERE e.attachmentsCleaned=false AND e.state=it.aboutbits.springboot.emailservice.lib.EmailState.SENT + """, Email.class + ) .setHint("jakarta.persistence.fetchgraph", entityGraph) .getResultList(); } diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/SendScheduledEmails.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/SendScheduledEmails.java index 107512b..f1789d0 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/SendScheduledEmails.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/SendScheduledEmails.java @@ -4,6 +4,7 @@ import it.aboutbits.springboot.emailservice.lib.EmailSchedulerCallback; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.jspecify.annotations.NullMarked; import org.springframework.scheduling.annotation.Scheduled; import java.time.Duration; @@ -11,6 +12,7 @@ @RequiredArgsConstructor @Log4j2 +@NullMarked public class SendScheduledEmails { private static final String JOB_DESCRIPTION = "Sending open and failed email notifications."; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/UnavailableAttachmentDataSource.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/UnavailableAttachmentDataSource.java index 6d7bd52..f2ba789 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/UnavailableAttachmentDataSource.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/UnavailableAttachmentDataSource.java @@ -2,9 +2,11 @@ import it.aboutbits.springboot.emailservice.lib.AttachmentDataSource; import it.aboutbits.springboot.emailservice.lib.exception.AttachmentException; +import org.jspecify.annotations.NullMarked; import java.io.InputStream; +@NullMarked public final class UnavailableAttachmentDataSource implements AttachmentDataSource { @Override public InputStream getAttachmentPayload(long fileReference) throws AttachmentException { diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java deleted file mode 100644 index 62e090e..0000000 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package it.aboutbits.springboot.emailservice.lib.application; - -import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java index f836e33..e59fb36 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java @@ -1,5 +1,8 @@ package it.aboutbits.springboot.emailservice.lib.exception; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked public class AttachmentException extends Exception { public AttachmentException() { super(); @@ -17,7 +20,12 @@ public AttachmentException(final Throwable cause) { super(cause); } - protected AttachmentException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) { + protected AttachmentException( + final String message, + final Throwable cause, + final boolean enableSuppression, + final boolean writableStackTrace + ) { super(message, cause, enableSuppression, writableStackTrace); } } diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java index 9bc9855..408e28e 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java @@ -1,5 +1,8 @@ package it.aboutbits.springboot.emailservice.lib.exception; +import org.jspecify.annotations.NullUnmarked; + +@NullUnmarked public class EmailException extends Exception { public EmailException() { super(); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java index 4c9b6fc..ba3100c 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/jpa/EmailRepository.java @@ -3,7 +3,7 @@ import it.aboutbits.springboot.emailservice.lib.EmailState; import it.aboutbits.springboot.emailservice.lib.model.Email; -import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.NullMarked; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.EntityGraph; @@ -13,19 +13,17 @@ import java.util.List; import java.util.Optional; +@NullMarked public interface EmailRepository extends JpaRepository { @Override - @NonNull @EntityGraph(value = Email.DEFAULT_ENTITY_GRAPH) - Optional findById(@NonNull Long id); + Optional findById(Long id); @Override - @NonNull @EntityGraph(value = Email.DEFAULT_ENTITY_GRAPH) - List findAllById(@NonNull Iterable ids); + List findAllById(Iterable ids); @Override - @NonNull @EntityGraph(value = Email.DEFAULT_ENTITY_GRAPH) List findAll(); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java index 19cf34d..326d8f1 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java @@ -21,7 +21,7 @@ import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.UpdateTimestamp; import org.hibernate.type.SqlTypes; -import org.jspecify.annotations.Nullable; +import org.jspecify.annotations.NullUnmarked; import java.time.OffsetDateTime; import java.util.List; @@ -43,6 +43,7 @@ @NoArgsConstructor @Table(name = "email_service_emails") @NamedEntityGraph(name = DEFAULT_ENTITY_GRAPH, attributeNodes = @NamedAttributeNode("attachments")) +@NullUnmarked public class Email { public static final String DEFAULT_ENTITY_GRAPH = "graph.EmailServiceEmail.default"; @@ -58,9 +59,7 @@ public class Email { private String fromAddress; private String fromName; - @Nullable private String replyToAddress; - @Nullable private String replyToName; @JdbcTypeCode(SqlTypes.JSON) diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/EmailAttachment.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/EmailAttachment.java index 9de7abb..b089bb9 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/EmailAttachment.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/EmailAttachment.java @@ -9,11 +9,13 @@ import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; +import org.jspecify.annotations.NullUnmarked; @Entity @Getter @Setter @Table(name = "email_service_email_attachments") +@NullUnmarked public class EmailAttachment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java deleted file mode 100644 index 5e3a1e3..0000000 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package it.aboutbits.springboot.emailservice.lib; - -import org.jspecify.annotations.NullMarked; From 7c96d2fd6e2ae7b8450c30adc7ebc4fb00267566 Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Thu, 6 Aug 2026 14:06:47 +0200 Subject: [PATCH 4/6] add ArchitectureTest, use FakerExtended and add annotations --- pom.xml | 5 +++++ .../springboot/emailservice/lib/EmailState.java | 3 +++ .../springboot/emailservice/ArchitectureTest.java | 15 +++++++++++++++ .../springboot/emailservice/TestApplication.java | 2 ++ .../lib/application/ManageEmailTest.java | 2 ++ .../lib/application/QueryEmailTest.java | 2 ++ .../support/database/PostgresTestcontainer.java | 2 ++ .../support/database/factory/EmailFactory.java | 6 ++++-- 8 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/test/java/it/aboutbits/springboot/emailservice/ArchitectureTest.java diff --git a/pom.xml b/pom.xml index 3ccea57..214ad04 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,11 @@ archunit-toolbox test + + it.aboutbits + spring-boot-testing + 2.5.0 + diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailState.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailState.java index f053425..4d2ed5d 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailState.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/EmailState.java @@ -1,5 +1,8 @@ package it.aboutbits.springboot.emailservice.lib; +import org.jspecify.annotations.NullMarked; + +@NullMarked public enum EmailState { PENDING, SENT, diff --git a/src/test/java/it/aboutbits/springboot/emailservice/ArchitectureTest.java b/src/test/java/it/aboutbits/springboot/emailservice/ArchitectureTest.java new file mode 100644 index 0000000..48caab5 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/emailservice/ArchitectureTest.java @@ -0,0 +1,15 @@ +package it.aboutbits.springboot.emailservice; + +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.CacheMode; +import it.aboutbits.archunit.toolbox.ArchitectureTestBase; +import org.jspecify.annotations.NullMarked; + +@AnalyzeClasses( + packages = ArchitectureTest.PACKAGE, + cacheMode = CacheMode.PER_CLASS +) +@NullMarked +class ArchitectureTest extends ArchitectureTestBase { + static final String PACKAGE = "it.aboutbits.springboot.emailservice"; +} diff --git a/src/test/java/it/aboutbits/springboot/emailservice/TestApplication.java b/src/test/java/it/aboutbits/springboot/emailservice/TestApplication.java index 20454aa..5d36525 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/TestApplication.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/TestApplication.java @@ -1,11 +1,13 @@ package it.aboutbits.springboot.emailservice; +import org.jspecify.annotations.NullMarked; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SuppressWarnings("checkstyle:HideUtilityClassConstructor") @SpringBootApplication @EnableEmailService +@NullMarked public class TestApplication { public static void main(final String[] args) { SpringApplication.run(TestApplication.class, args); diff --git a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java index 3a5e1a4..1c6b9e8 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmailTest.java @@ -6,6 +6,7 @@ import it.aboutbits.springboot.emailservice.lib.exception.EmailException; import it.aboutbits.springboot.emailservice.support.database.WithPostgres; import jakarta.mail.internet.MimeMessage; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +31,7 @@ @SpringBootTest @WithPostgres +@NullMarked class ManageEmailTest { @MockitoSpyBean JavaMailSender javaMailSender; diff --git a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java index a99be68..ff400c7 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/lib/application/QueryEmailTest.java @@ -5,6 +5,7 @@ import it.aboutbits.springboot.emailservice.lib.jpa.EmailRepository; import it.aboutbits.springboot.emailservice.support.database.WithPostgres; import it.aboutbits.springboot.emailservice.support.database.factory.EmailFactory; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -19,6 +20,7 @@ @SpringBootTest @WithPostgres +@NullMarked class QueryEmailTest { @MockitoBean JavaMailSender javaMailSender; diff --git a/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java b/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java index cb5c2ae..167c93d 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/support/database/PostgresTestcontainer.java @@ -2,6 +2,7 @@ import lombok.Data; import lombok.extern.log4j.Log4j2; +import org.jspecify.annotations.NullMarked; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -17,6 +18,7 @@ import java.util.StringJoiner; @Log4j2 +@NullMarked public class PostgresTestcontainer implements BeforeAllCallback, AfterEachCallback { public static final PostgreSQLContainer POSTGRES_CONTAINER; private static final Set TABLES_TO_IGNORE = Set.of( diff --git a/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java b/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java index 6d7af82..1c37b19 100644 --- a/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java +++ b/src/test/java/it/aboutbits/springboot/emailservice/support/database/factory/EmailFactory.java @@ -2,13 +2,15 @@ import it.aboutbits.springboot.emailservice.lib.EmailState; import it.aboutbits.springboot.emailservice.lib.model.Email; -import net.datafaker.Faker; +import it.aboutbits.springboot.testing.testdata.FakerExtended; +import org.jspecify.annotations.NullMarked; import java.time.OffsetDateTime; import java.util.List; +@NullMarked public final class EmailFactory { - private static final Faker FAKER = new Faker(); + private static final FakerExtended FAKER = new FakerExtended(); private EmailFactory() { } From aad66cb1bca3c27e33348c78181f5c7647d1baeb Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Thu, 6 Aug 2026 19:44:49 +0200 Subject: [PATCH 5/6] requested changes --- .../lib/application/ManageEmail.java | 3 ++- .../lib/exception/AttachmentException.java | 18 +++++++++--------- .../lib/exception/EmailException.java | 4 ++-- .../emailservice/lib/model/Email.java | 6 ++++++ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java index 809a5f8..2ba2a4c 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java @@ -72,7 +72,8 @@ public EmailDto sendOrFail(@Valid EmailParameter parameter) throws EmailExceptio var savedEmail = send(email); if (savedEmail.hasFailed()) { - throw new EmailException(savedEmail.getErrorMessage()); + var errorMessage = savedEmail.getErrorMessage(); + throw new EmailException(errorMessage != null ? errorMessage : ""); } return emailMapper.toDto(savedEmail); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java index e59fb36..837bcb2 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/AttachmentException.java @@ -1,30 +1,30 @@ package it.aboutbits.springboot.emailservice.lib.exception; -import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.NullMarked; -@NullUnmarked +@NullMarked public class AttachmentException extends Exception { public AttachmentException() { super(); } - public AttachmentException(final String message) { + public AttachmentException(String message) { super(message); } - public AttachmentException(final String message, final Throwable cause) { + public AttachmentException(String message, Throwable cause) { super(message, cause); } - public AttachmentException(final Throwable cause) { + public AttachmentException(Throwable cause) { super(cause); } protected AttachmentException( - final String message, - final Throwable cause, - final boolean enableSuppression, - final boolean writableStackTrace + String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace ) { super(message, cause, enableSuppression, writableStackTrace); } diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java index 408e28e..8b890cb 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/exception/EmailException.java @@ -1,8 +1,8 @@ package it.aboutbits.springboot.emailservice.lib.exception; -import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.NullMarked; -@NullUnmarked +@NullMarked public class EmailException extends Exception { public EmailException() { super(); diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java index 326d8f1..88a990e 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/model/Email.java @@ -22,6 +22,7 @@ import org.hibernate.annotations.UpdateTimestamp; import org.hibernate.type.SqlTypes; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.time.OffsetDateTime; import java.util.List; @@ -59,7 +60,9 @@ public class Email { private String fromAddress; private String fromName; + @Nullable private String replyToAddress; + @Nullable private String replyToName; @JdbcTypeCode(SqlTypes.JSON) @@ -75,9 +78,12 @@ public class Email { private boolean attachmentsCleaned = false; private OffsetDateTime scheduledAt; + @Nullable private OffsetDateTime sentAt; + @Nullable private OffsetDateTime errorAt; + @Nullable private String errorMessage; @CreationTimestamp From 7da0c7a2ade0010d50db2b6c5b0e7295ab8331d3 Mon Sep 17 00:00:00 2001 From: Jonas Mayr Date: Mon, 10 Aug 2026 08:32:10 +0200 Subject: [PATCH 6/6] requested change --- .../springboot/emailservice/lib/application/ManageEmail.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java index 2ba2a4c..3346aac 100644 --- a/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java +++ b/src/main/java/it/aboutbits/springboot/emailservice/lib/application/ManageEmail.java @@ -72,8 +72,8 @@ public EmailDto sendOrFail(@Valid EmailParameter parameter) throws EmailExceptio var savedEmail = send(email); if (savedEmail.hasFailed()) { - var errorMessage = savedEmail.getErrorMessage(); - throw new EmailException(errorMessage != null ? errorMessage : ""); + throw new EmailException("Failed to send email [id=%s, providerMessage=%s]" + .formatted(savedEmail.getId(), savedEmail.getErrorMessage())); } return emailMapper.toDto(savedEmail);