Skip to content

Commit bc471e0

Browse files
authored
archunit & jspecify (#31)
* spring boot 4 update * fix pom * add archunit and jspecify * reformat code * extract common archunit rules * cleanup * update deps
1 parent 9857d7c commit bc471e0

62 files changed

Lines changed: 491 additions & 286 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.idea/codeStyles/Project.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lombok.config

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
config.stopBubbling = true
2+
3+
# Required for NullAway to work
4+
lombok.addLombokGeneratedAnnotation = true
5+
6+
# Copy JSpecify annotations to generated code (constructors, getters, setters)
7+
lombok.copyableAnnotations += org.jspecify.annotations.Nullable
8+
lombok.copyableAnnotations += org.jspecify.annotations.NonNull
9+
lombok.addNullAnnotations = jspecify

pom.xml

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
34
<modelVersion>4.0.0</modelVersion>
45

56
<parent>
67
<groupId>org.springframework.boot</groupId>
78
<artifactId>spring-boot-starter-parent</artifactId>
8-
<version>4.0.0</version>
9+
<version>4.0.1</version>
910
<relativePath/> <!-- lookup parent from repository -->
1011
</parent>
1112

@@ -16,14 +17,21 @@
1617

1718
<properties>
1819
<java.version>25</java.version>
20+
<errorprone.version>2.46.0</errorprone.version>
21+
<nullaway.version>0.12.15</nullaway.version>
1922
</properties>
2023

2124
<dependencyManagement>
2225
<dependencies>
2326
<dependency>
2427
<groupId>it.aboutbits</groupId>
2528
<artifactId>spring-boot-toolbox</artifactId>
26-
<version>2.0.0</version>
29+
<version>2.1.0-RC1</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>it.aboutbits</groupId>
33+
<artifactId>archunit-toolbox</artifactId>
34+
<version>1.0.0-RC1</version>
2735
</dependency>
2836
</dependencies>
2937
</dependencyManagement>
@@ -72,6 +80,26 @@
7280
<artifactId>datafaker</artifactId>
7381
<version>2.5.3</version>
7482
</dependency>
83+
84+
<!-- https://mvnrepository.com/artifact/com.tngtech.archunit/archunit-junit5 -->
85+
<dependency>
86+
<groupId>com.tngtech.archunit</groupId>
87+
<artifactId>archunit-junit5</artifactId>
88+
<version>1.4.1</version>
89+
<scope>test</scope>
90+
</dependency>
91+
<dependency>
92+
<groupId>com.tngtech.archunit</groupId>
93+
<artifactId>archunit-junit5-api</artifactId>
94+
<version>1.4.1</version>
95+
<scope>compile</scope>
96+
</dependency>
97+
98+
<dependency>
99+
<groupId>it.aboutbits</groupId>
100+
<artifactId>archunit-toolbox</artifactId>
101+
<scope>test</scope>
102+
</dependency>
75103
</dependencies>
76104

77105
<build>
@@ -89,7 +117,36 @@
89117
<artifactId>lombok</artifactId>
90118
<version>${lombok.version}</version>
91119
</path>
120+
<path>
121+
<groupId>com.google.errorprone</groupId>
122+
<artifactId>error_prone_core</artifactId>
123+
<version>${errorprone.version}</version>
124+
</path>
125+
<path>
126+
<groupId>com.uber.nullaway</groupId>
127+
<artifactId>nullaway</artifactId>
128+
<version>${nullaway.version}</version>
129+
</path>
92130
</annotationProcessorPaths>
131+
<compilerArgs>
132+
<arg>-Amapstruct.unmappedTargetPolicy=ERROR</arg>
133+
<arg>-XDcompilePolicy=simple</arg>
134+
<arg>--should-stop=ifError=FLOW</arg>
135+
<!-- @formatter:off -->
136+
<arg>-Xplugin:ErrorProne -XepDisableAllChecks -XepOpt:NullAway:AnnotatedPackages=it.aboutbits.springboot.testing -XepOpt:NullAway:JSpecifyMode=true -Xep:NullAway:ERROR -XepOpt:NullAway:ExcludedFieldAnnotations=org.mockito.InjectMocks,org.mockito.Mock</arg>
137+
<!-- @formatter:on -->
138+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
139+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
140+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
141+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
142+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
143+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
144+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
145+
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
146+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
147+
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
148+
</compilerArgs>
149+
<fork>true</fork>
93150
</configuration>
94151
</plugin>
95152
<plugin>

src/main/java/it/aboutbits/springboot/testing/.gitkeep

Whitespace-only changes.

src/main/java/it/aboutbits/springboot/testing/persistence/PersistenceAssert.java

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import it.aboutbits.springboot.toolbox.type.identity.Identified;
77
import jakarta.persistence.EntityManager;
88
import lombok.AccessLevel;
9-
import lombok.NonNull;
109
import lombok.RequiredArgsConstructor;
10+
import org.jspecify.annotations.NullMarked;
1111

1212
import java.util.Collection;
1313
import java.util.List;
@@ -18,6 +18,7 @@
1818
import static it.aboutbits.springboot.toolbox.util.CollectUtil.collectToSet;
1919
import static org.assertj.core.api.Assertions.assertThat;
2020

21+
@NullMarked
2122
public final class PersistenceAssert {
2223
private PersistenceAssert() {
2324
}
@@ -28,44 +29,32 @@ private static EntityManager getEntityManager() {
2829

2930
@SuppressWarnings("unused")
3031
public static <ID extends EntityId<?>, E extends Identified<ID> & ChangeAware, M extends Identified<ID> & ChangeAware> WriteOperationAsserter<ID, E, M> assertThatEntity(
31-
@NonNull E before,
32-
@NonNull Class<M> modelClass
32+
E before,
33+
Class<M> modelClass
3334
) {
3435
return new WriteOperationAsserter<>(getEntityManager(), List.of(before), modelClass);
3536
}
3637

3738
@SuppressWarnings("unused")
3839
public static <ID extends EntityId<?>, E extends Identified<ID> & ChangeAware, M extends Identified<ID> & ChangeAware> WriteOperationAsserter<ID, E, M> assertThatEntity(
39-
@NonNull Collection<E> before,
40-
@NonNull Class<M> modelClass
40+
Collection<E> before,
41+
Class<M> modelClass
4142
) {
4243
return new WriteOperationAsserter<>(getEntityManager(), before, modelClass);
4344
}
4445

45-
/**
46-
* @deprecated Use {@link #assertThatEntityId(EntityId, Class)} instead.
47-
*/
48-
@Deprecated
49-
@SuppressWarnings("unused")
50-
public static <ID extends EntityId<?>, M extends Identified<ID>> WriteOperationIdAsserter<ID, M> assertThatEntity(
51-
@NonNull ID id,
52-
@NonNull Class<M> modelClass
53-
) {
54-
return new WriteOperationIdAsserter<>(getEntityManager(), List.of(id), modelClass);
55-
}
56-
5746
@SuppressWarnings("unused")
5847
public static <ID extends EntityId<?>, M extends Identified<ID>> WriteOperationIdAsserter<ID, M> assertThatEntityId(
59-
@NonNull ID id,
60-
@NonNull Class<M> modelClass
48+
ID id,
49+
Class<M> modelClass
6150
) {
6251
return new WriteOperationIdAsserter<>(getEntityManager(), List.of(id), modelClass);
6352
}
6453

6554
@SuppressWarnings("unused")
6655
public static <ID extends EntityId<?>, M extends Identified<ID>> WriteOperationIdAsserter<ID, M> assertThatEntityId(
67-
@NonNull Collection<ID> id,
68-
@NonNull Class<M> modelClass
56+
Collection<ID> id,
57+
Class<M> modelClass
6958
) {
7059
return new WriteOperationIdAsserter<>(getEntityManager(), id, modelClass);
7160
}
@@ -154,6 +143,8 @@ public void hasBeenUpdatedInDatabase() {
154143

155144
for (var savedInstance : savedInstances) {
156145
var originalEntity = originalByIdMap.get(savedInstance.getId());
146+
Objects.requireNonNull(originalEntity);
147+
157148
assertThat(originalEntity).isNotNull();
158149

159150
assertThat(
@@ -177,6 +168,7 @@ public void hasNotChangedInDatabase() {
177168

178169
for (var savedInstance : savedInstances) {
179170
var originalEntity = originalByIdMap.get(savedInstance.getId());
171+
Objects.requireNonNull(originalEntity);
180172

181173
assertThat(savedInstance).isNotNull();
182174
assertThat(originalEntity).isNotNull();
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package it.aboutbits.springboot.testing.spring;
22

3-
import lombok.NonNull;
3+
import org.jspecify.annotations.NullMarked;
4+
import org.jspecify.annotations.Nullable;
45
import org.springframework.context.ApplicationContext;
56
import org.springframework.context.ApplicationContextAware;
67
import org.springframework.stereotype.Component;
78

89
@Component
10+
@NullMarked
911
public class BeanAccessor implements ApplicationContextAware {
12+
@Nullable
1013
private static ApplicationContext applicationContext;
1114

12-
public static <T> T getBean(@NonNull Class<T> clazz) {
15+
public static <T> T getBean(Class<T> clazz) {
1316
if (applicationContext == null) {
1417
throw new IllegalStateException("ApplicationContext is not set. Did you @EnableBeanAccessor?");
1518
}
1619
return applicationContext.getBean(clazz);
1720
}
1821

1922
@Override
20-
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
23+
public void setApplicationContext(ApplicationContext applicationContext) {
2124
BeanAccessor.applicationContext = applicationContext;
2225
}
2326
}

src/main/java/it/aboutbits/springboot/testing/testdata/base/AllTestDataReader.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package it.aboutbits.springboot.testing.testdata.base;
22

3-
import lombok.NonNull;
3+
import org.jspecify.annotations.NullMarked;
44

55
import java.util.Comparator;
66
import java.util.HashSet;
@@ -9,17 +9,22 @@
99
import java.util.function.Function;
1010
import java.util.function.Predicate;
1111

12+
@SuppressWarnings({"java:S119"})
13+
@NullMarked
1214
public abstract class AllTestDataReader<ITEM> {
15+
@SuppressWarnings("unused")
1316
public ITEM returnFirst() {
14-
return (ITEM) this.returnAll().getFirst();
17+
return this.returnAll().getFirst();
1518
}
1619

20+
@SuppressWarnings("unused")
1721
public List<ITEM> returnAll() {
1822
return this.fetch();
1923
}
2024

2125
@SafeVarargs
22-
public final List<ITEM> returnSorted(@NonNull Comparator<ITEM>... comparators) {
26+
@SuppressWarnings("unused")
27+
public final List<ITEM> returnSorted(Comparator<ITEM>... comparators) {
2328
if (comparators.length == 0) {
2429
throw new IllegalArgumentException("At least one comparator must be provided");
2530
}
@@ -33,8 +38,8 @@ public final List<ITEM> returnSorted(@NonNull Comparator<ITEM>... comparators) {
3338
}
3439

3540
@SafeVarargs
36-
@SuppressWarnings("unchecked")
37-
public final <U extends Comparable<? super U>> List<ITEM> returnSorted(@NonNull Function<ITEM, ? extends Comparable<?>>... comparators) {
41+
@SuppressWarnings({"unchecked", "unused"})
42+
public final <U extends Comparable<? super U>> List<ITEM> returnSorted(Function<ITEM, ? extends Comparable<?>>... comparators) {
3843
if (comparators.length == 0) {
3944
throw new IllegalArgumentException("At least one comparator must be provided");
4045
}
@@ -47,22 +52,24 @@ public final <U extends Comparable<? super U>> List<ITEM> returnSorted(@NonNull
4752
return returnAll().stream().sorted(combinedComparator).toList();
4853
}
4954

50-
public <U extends Comparable<? super U>> AllAndFiltered<ITEM> returnFiltered(@NonNull Predicate<ITEM> predicate) {
55+
@SuppressWarnings("unused")
56+
public <U extends Comparable<? super U>> AllAndFiltered<ITEM> returnFiltered(Predicate<ITEM> predicate) {
5157
var all = this.returnAll();
52-
return new AllAndFiltered<ITEM>(
58+
return new AllAndFiltered<>(
5359
all,
5460
all.stream().filter(predicate).toList(),
5561
all.stream().filter(item -> !predicate.test(item)).toList()
5662
);
5763
}
5864

65+
@SuppressWarnings("unused")
5966
public Set<ITEM> returnSet() {
6067
return new HashSet<>(this.returnAll());
6168
}
6269

6370
protected abstract List<ITEM> fetch();
6471

65-
public record AllAndFiltered<T>(@NonNull List<T> all, @NonNull List<T> filtered, @NonNull List<T> other) {
72+
public record AllAndFiltered<T>(List<T> all, List<T> filtered, List<T> other) {
6673

6774
}
6875
}

src/main/java/it/aboutbits/springboot/testing/testdata/base/FakerExtended.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22

33
import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal;
44
import it.aboutbits.springboot.toolbox.type.identity.EntityId;
5-
import lombok.NonNull;
65
import net.datafaker.Faker;
76
import net.datafaker.service.FakeValuesService;
87
import net.datafaker.service.FakerContext;
98
import net.datafaker.service.RandomService;
9+
import org.jspecify.annotations.NullMarked;
1010

1111
import java.util.Locale;
1212
import java.util.Random;
1313
import java.util.function.Function;
1414
import java.util.function.LongFunction;
1515

16+
@NullMarked
1617
public class FakerExtended extends Faker {
1718
public FakerExtended() {
1819
super();
@@ -38,27 +39,27 @@ public FakerExtended(FakeValuesService fakeValuesService, FakerContext context)
3839
super(fakeValuesService, context);
3940
}
4041

41-
public <T extends Enum<?>> T randomEnumValue(@NonNull Class<T> enumClass) {
42+
public <T extends Enum<?>> T randomEnumValue(Class<T> enumClass) {
4243
var values = enumClass.getEnumConstants();
4344
if (values.length == 0) {
4445
throw new IllegalArgumentException("Enum class must have at least one value");
4546
}
4647
return values[this.random().nextInt(values.length)];
4748
}
4849

49-
public <T extends EntityId<Long>> T randomEntityId(@NonNull LongFunction<T> constructor) {
50+
public <T extends EntityId<Long>> T randomEntityId(LongFunction<T> constructor) {
5051
return constructor.apply(
5152
super.random().nextInt(9999999)
5253
);
5354
}
5455

55-
public <T extends EntityId<String>> T randomEntityId(@NonNull Function<String, T> constructor) {
56+
public <T extends EntityId<String>> T randomEntityId(Function<String, T> constructor) {
5657
return constructor.apply(
5758
super.internet().uuid()
5859
);
5960
}
6061

61-
public String unique(@NonNull String value) {
62+
public String unique(String value) {
6263
return value + "_" + super.random().nextInt(9999999);
6364
}
6465

0 commit comments

Comments
 (0)