Skip to content

Commit 7e937cb

Browse files
committed
Add JSpecify and NullAway
Signed-off-by: Stefano Cordio <[email protected]>
1 parent f63ddce commit 7e937cb

File tree

7 files changed

+115
-73
lines changed

7 files changed

+115
-73
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
2+
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
3+
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
4+
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
5+
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
6+
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
7+
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
8+
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
9+
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
10+
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

spring-batch-notion/pom.xml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,31 @@ limitations under the License.
160160
<artifactId>maven-compiler-plugin</artifactId>
161161
<configuration>
162162
<compilerArgs>
163-
<arg>-Xlint:all,-varargs</arg>
163+
<compilerArg>-Xlint:all,-varargs</compilerArg>
164+
<!-- https://errorprone.info/docs/installation#maven -->
165+
<compilerArg>-XDcompilePolicy=simple</compilerArg>
166+
<compilerArg>--should-stop=ifError=FLOW</compilerArg>
167+
<compilerArg>
168+
-Xplugin:ErrorProne
169+
-XepDisableAllChecks
170+
<!-- Check JSpecify annotations -->
171+
-Xep:NullAway
172+
-XepOpt:NullAway:OnlyNullMarked
173+
-XepOpt:NullAway:SuppressionNameAliases=DataFlowIssue
174+
</compilerArg>
164175
</compilerArgs>
176+
<annotationProcessorPaths>
177+
<path>
178+
<groupId>com.google.errorprone</groupId>
179+
<artifactId>error_prone_core</artifactId>
180+
<version>2.43.0</version>
181+
</path>
182+
<path>
183+
<groupId>com.uber.nullaway</groupId>
184+
<artifactId>nullaway</artifactId>
185+
<version>0.12.11</version>
186+
</path>
187+
</annotationProcessorPaths>
165188
<failOnWarning>true</failOnWarning>
166189
</configuration>
167190
</plugin>

spring-batch-notion/src/main/java/org/springframework/batch/extensions/notion/NotionDatabaseItemReader.java

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
import notion.api.v1.model.pages.PageProperty;
2626
import notion.api.v1.model.pages.PageProperty.RichText;
2727
import notion.api.v1.request.databases.QueryDatabaseRequest;
28+
import org.jspecify.annotations.Nullable;
2829
import org.springframework.batch.extensions.notion.mapping.PropertyMapper;
2930
import org.springframework.batch.infrastructure.item.ExecutionContext;
3031
import org.springframework.batch.infrastructure.item.ItemReader;
3132
import org.springframework.batch.infrastructure.item.data.AbstractPaginatedDataItemReader;
32-
import org.springframework.beans.factory.InitializingBean;
3333
import org.springframework.util.Assert;
3434

3535
import java.util.Collections;
@@ -57,40 +57,47 @@
5757
* @author Stefano Cordio
5858
* @param <T> Type of item to be read
5959
*/
60-
public class NotionDatabaseItemReader<T> extends AbstractPaginatedDataItemReader<T> implements InitializingBean {
60+
public class NotionDatabaseItemReader<T> extends AbstractPaginatedDataItemReader<T> {
6161

6262
private static final String DEFAULT_BASE_URL = "https://api.notion.com/v1";
6363

6464
private static final int DEFAULT_PAGE_SIZE = 100;
6565

66-
private String baseUrl;
66+
private final String token;
6767

68-
private String token;
68+
private final String databaseId;
6969

70-
private String databaseId;
70+
private final PropertyMapper<T> propertyMapper;
7171

72-
private PropertyMapper<T> propertyMapper;
72+
private String baseUrl;
7373

74-
private QueryTopLevelFilter filter;
74+
private @Nullable QueryTopLevelFilter filter;
7575

76-
private List<QuerySort> sorts;
76+
private @Nullable List<QuerySort> sorts;
7777

78-
private NotionClient client;
78+
private @Nullable NotionClient client;
7979

8080
private boolean hasMore;
8181

82-
private String nextCursor;
82+
private @Nullable String nextCursor;
8383

8484
/**
8585
* Create a new {@link NotionDatabaseItemReader} with the following defaults:
8686
* <ul>
8787
* <li>{@code baseUrl} = {@value #DEFAULT_BASE_URL}</li>
8888
* <li>{@code pageSize} = {@value #DEFAULT_PAGE_SIZE}</li>
8989
* </ul>
90+
* @param token the Notion integration token
91+
* @param databaseId UUID of the database to read from
92+
* @param propertyMapper the {@link PropertyMapper} responsible for mapping Notion
93+
* item properties into a Java object
9094
*/
91-
public NotionDatabaseItemReader() {
95+
public NotionDatabaseItemReader(String token, String databaseId, PropertyMapper<T> propertyMapper) {
9296
this.baseUrl = DEFAULT_BASE_URL;
9397
this.pageSize = DEFAULT_PAGE_SIZE;
98+
this.token = Objects.requireNonNull(token);
99+
this.databaseId = Objects.requireNonNull(databaseId);
100+
this.propertyMapper = Objects.requireNonNull(propertyMapper);
94101
}
95102

96103
/**
@@ -106,37 +113,6 @@ public void setBaseUrl(String baseUrl) {
106113
this.baseUrl = Objects.requireNonNull(baseUrl);
107114
}
108115

109-
/**
110-
* The Notion integration token.
111-
* <p>
112-
* Always required.
113-
* @param token the token
114-
*/
115-
public void setToken(String token) {
116-
this.token = Objects.requireNonNull(token);
117-
}
118-
119-
/**
120-
* UUID of the database to read from.
121-
* <p>
122-
* Always required.
123-
* @param databaseId the database UUID
124-
*/
125-
public void setDatabaseId(String databaseId) {
126-
this.databaseId = Objects.requireNonNull(databaseId);
127-
}
128-
129-
/**
130-
* The {@link PropertyMapper} responsible for mapping Notion item properties into a
131-
* Java object.
132-
* <p>
133-
* Always required.
134-
* @param propertyMapper the property mapper
135-
*/
136-
public void setPropertyMapper(PropertyMapper<T> propertyMapper) {
137-
this.propertyMapper = Objects.requireNonNull(propertyMapper);
138-
}
139-
140116
/**
141117
* {@link Filter} condition to limit the returned items.
142118
* <p>
@@ -190,6 +166,7 @@ protected Iterator<T> doPageRead() {
190166
request.setStartCursor(nextCursor);
191167
request.setPageSize(pageSize);
192168

169+
@SuppressWarnings("DataFlowIssue")
193170
QueryResults queryResults = client.queryDatabase(request);
194171

195172
hasMore = queryResults.getHasMore();
@@ -237,6 +214,7 @@ protected void doOpen() {
237214
/**
238215
* {@inheritDoc}
239216
*/
217+
@SuppressWarnings("DataFlowIssue")
240218
@Override
241219
protected void doClose() {
242220
client.close();
@@ -255,14 +233,4 @@ protected void jumpToItem(int itemIndex) throws Exception {
255233
}
256234
}
257235

258-
/**
259-
* {@inheritDoc}
260-
*/
261-
@Override
262-
public void afterPropertiesSet() {
263-
Assert.state(token != null, "'token' must be set");
264-
Assert.state(databaseId != null, "'databaseId' must be set");
265-
Assert.state(propertyMapper != null, "'propertyMapper' must be set");
266-
}
267-
268236
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Spring Batch extension for Notion.
19+
*/
20+
@NullMarked
21+
package org.springframework.batch.extensions.notion;
22+
23+
import org.jspecify.annotations.NullMarked;

spring-batch-notion/src/test/java/org/springframework/batch/extensions/notion/it/pagination/MultiplePagesDescendingTests.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.batch.extensions.notion.NotionDatabaseItemReader;
2727
import org.springframework.batch.extensions.notion.Sort;
2828
import org.springframework.batch.extensions.notion.it.IntegrationTest;
29+
import org.springframework.batch.extensions.notion.it.pagination.MultiplePagesDescendingTests.PaginatedDescendingJob.Item;
2930
import org.springframework.batch.extensions.notion.mapping.RecordPropertyMapper;
3031
import org.springframework.batch.infrastructure.item.support.ListItemWriter;
3132
import org.springframework.batch.test.JobOperatorTestUtils;
@@ -74,7 +75,7 @@ class MultiplePagesDescendingTests {
7475
JobOperatorTestUtils jobOperator;
7576

7677
@Autowired
77-
ListItemWriter<PaginatedDescendingJob.Item> itemWriter;
78+
ListItemWriter<Item> itemWriter;
7879

7980
@Test
8081
void should_succeed() throws Exception {
@@ -110,9 +111,9 @@ void should_succeed() throws Exception {
110111

111112
then(itemWriter.getWrittenItems()).asInstanceOf(LIST)
112113
.containsExactly( //
113-
new PaginatedDescendingJob.Item("Name string", "123456"), //
114-
new PaginatedDescendingJob.Item("Another name string", "0987654321"), //
115-
new PaginatedDescendingJob.Item("", "abc-1234"));
114+
new Item("Name string", "123456"), //
115+
new Item("Another name string", "0987654321"), //
116+
new Item("", "abc-1234"));
116117
}
117118

118119
@SpringBootApplication
@@ -137,17 +138,13 @@ Step step(JobRepository jobRepository) {
137138

138139
@Bean
139140
NotionDatabaseItemReader<Item> itemReader() {
140-
NotionDatabaseItemReader<Item> reader = new NotionDatabaseItemReader<>();
141+
NotionDatabaseItemReader<Item> reader = new NotionDatabaseItemReader<>("token", DATABASE_ID.toString(),
142+
new RecordPropertyMapper<>());
141143

142144
reader.setSaveState(false);
143-
144-
reader.setToken("token");
145145
reader.setBaseUrl(wiremockBaseUrl);
146-
reader.setDatabaseId(DATABASE_ID.toString());
147-
148146
reader.setPageSize(PAGE_SIZE);
149147
reader.setSorts(Sort.by("Name", DESCENDING));
150-
reader.setPropertyMapper(new RecordPropertyMapper<>());
151148

152149
return reader;
153150
}

spring-batch-notion/src/test/java/org/springframework/batch/extensions/notion/it/pagination/MultiplePagesTests.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.batch.extensions.notion.it.pagination;
1717

1818
import org.json.JSONObject;
19+
import org.jspecify.annotations.NonNull;
1920
import org.junit.jupiter.api.Test;
2021
import org.springframework.batch.core.job.Job;
2122
import org.springframework.batch.core.job.JobExecution;
@@ -25,6 +26,7 @@
2526
import org.springframework.batch.core.step.builder.StepBuilder;
2627
import org.springframework.batch.extensions.notion.NotionDatabaseItemReader;
2728
import org.springframework.batch.extensions.notion.it.IntegrationTest;
29+
import org.springframework.batch.extensions.notion.it.pagination.MultiplePagesTests.PaginatedJob.Item;
2830
import org.springframework.batch.extensions.notion.mapping.RecordPropertyMapper;
2931
import org.springframework.batch.infrastructure.item.support.ListItemWriter;
3032
import org.springframework.batch.test.JobOperatorTestUtils;
@@ -71,7 +73,7 @@ class MultiplePagesTests {
7173
JobOperatorTestUtils jobOperator;
7274

7375
@Autowired
74-
ListItemWriter<PaginatedJob.Item> itemWriter;
76+
ListItemWriter<Item> itemWriter;
7577

7678
@Test
7779
void should_succeed() throws Exception {
@@ -107,9 +109,9 @@ void should_succeed() throws Exception {
107109

108110
then(itemWriter.getWrittenItems()).asInstanceOf(LIST)
109111
.containsExactly( //
110-
new PaginatedJob.Item("Another name string", "0987654321"), //
111-
new PaginatedJob.Item("Name string", "123456"), //
112-
new PaginatedJob.Item("", "abc-1234"));
112+
new Item("Another name string", "0987654321"), //
113+
new Item("Name string", "123456"), //
114+
new Item("", "abc-1234"));
113115
}
114116

115117
@SpringBootApplication
@@ -134,16 +136,12 @@ Step step(JobRepository jobRepository) {
134136

135137
@Bean
136138
NotionDatabaseItemReader<Item> itemReader() {
137-
NotionDatabaseItemReader<Item> reader = new NotionDatabaseItemReader<>();
139+
NotionDatabaseItemReader<Item> reader = new NotionDatabaseItemReader<>("token", DATABASE_ID.toString(),
140+
new RecordPropertyMapper<>());
138141

139142
reader.setSaveState(false);
140-
141-
reader.setToken("token");
142143
reader.setBaseUrl(wiremockBaseUrl);
143-
reader.setDatabaseId(DATABASE_ID.toString());
144-
145144
reader.setPageSize(PAGE_SIZE);
146-
reader.setPropertyMapper(new RecordPropertyMapper<>());
147145

148146
return reader;
149147
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Tests for Spring Batch Notion.
19+
*/
20+
@NullMarked
21+
package org.springframework.batch.extensions.notion;
22+
23+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)