Skip to content

Commit 13add88

Browse files
committed
Add nullability annotations
1 parent 531216e commit 13add88

29 files changed

+439
-264
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@
612612
<version>${kotlin.version}</version>
613613
<extensions>true</extensions>
614614
<configuration>
615+
<javaParameters>true</javaParameters>
615616
<args>
616617
<arg>-Xjsr305=strict</arg> <!-- Enable strict mode for JSR-305 annotations -->
617618
</args>

server/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@
7979
<groupId>org.apache.commons</groupId>
8080
<artifactId>commons-lang3</artifactId>
8181
</dependency>
82+
<dependency>
83+
<groupId>org.jspecify</groupId>
84+
<artifactId>jspecify</artifactId>
85+
</dependency>
8286
<!-- Test Dependencies -->
8387
<dependency>
8488
<groupId>org.apache.httpcomponents</groupId>

server/src/main/java/com/adobe/testing/s3mock/service/BucketService.java

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import java.util.Objects;
6464
import java.util.UUID;
6565
import java.util.concurrent.ConcurrentHashMap;
66+
import org.jspecify.annotations.Nullable;
6667
import software.amazon.awssdk.utils.http.SdkHttpUtils;
6768

6869
public class BucketService {
@@ -99,8 +100,11 @@ public boolean doesBucketExist(String bucketName) {
99100
return bucketStore.doesBucketExist(bucketName);
100101
}
101102

102-
public ListAllMyBucketsResult listBuckets(Region bucketRegion, String continuationToken,
103-
Integer maxBuckets, String prefix) {
103+
public ListAllMyBucketsResult listBuckets(
104+
@Nullable Region bucketRegion,
105+
@Nullable String continuationToken,
106+
Integer maxBuckets,
107+
@Nullable String prefix) {
104108
String nextContinuationToken = null;
105109
var normalizedPrefix = prefix == null ? "" : prefix;
106110

@@ -139,12 +143,13 @@ public Bucket getBucket(String bucketName) {
139143
return Bucket.from(bucketStore.getBucketMetadata(bucketName));
140144
}
141145

142-
public Bucket createBucket(String bucketName,
146+
public Bucket createBucket(
147+
String bucketName,
143148
boolean objectLockEnabled,
144149
ObjectOwnership objectOwnership,
145150
String bucketRegion,
146-
BucketInfo bucketInfo,
147-
LocationInfo locationInfo) {
151+
@Nullable BucketInfo bucketInfo,
152+
@Nullable LocationInfo locationInfo) {
148153
return Bucket.from(
149154
bucketStore.createBucket(bucketName,
150155
objectLockEnabled,
@@ -213,8 +218,9 @@ public ObjectLockConfiguration getObjectLockConfiguration(String bucketName) {
213218
}
214219
}
215220

216-
public void setBucketLifecycleConfiguration(String bucketName,
217-
BucketLifecycleConfiguration configuration) {
221+
public void setBucketLifecycleConfiguration(
222+
String bucketName,
223+
@Nullable BucketLifecycleConfiguration configuration) {
218224
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
219225
bucketStore.storeBucketLifecycleConfiguration(bucketMetadata, configuration);
220226
}
@@ -233,7 +239,7 @@ public BucketLifecycleConfiguration getBucketLifecycleConfiguration(String bucke
233239
}
234240
}
235241

236-
public List<S3Object> getS3Objects(String bucketName, String prefix) {
242+
public List<S3Object> getS3Objects(String bucketName, @Nullable String prefix) {
237243
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
238244
var uuids = bucketStore.lookupKeysInBucket(prefix, bucketName);
239245
return uuids
@@ -300,13 +306,14 @@ public ListVersionsResult listVersions(String bucketName,
300306
);
301307
}
302308

303-
public ListBucketResultV2 listObjectsV2(String bucketName,
304-
String prefix,
305-
String delimiter,
306-
String encodingType,
307-
String startAfter,
309+
public ListBucketResultV2 listObjectsV2(
310+
String bucketName,
311+
@Nullable String prefix,
312+
@Nullable String delimiter,
313+
@Nullable String encodingType,
314+
@Nullable String startAfter,
308315
Integer maxKeys,
309-
String continuationToken,
316+
@Nullable String continuationToken,
310317
boolean fetchOwner) {
311318

312319
if (maxKeys == 0) {
@@ -396,8 +403,13 @@ public ListBucketResultV2 listObjectsV2(String bucketName,
396403
}
397404

398405
@Deprecated(since = "2.12.2", forRemoval = true)
399-
public ListBucketResult listObjectsV1(String bucketName, String prefix, String delimiter,
400-
String marker, String encodingType, Integer maxKeys) {
406+
public ListBucketResult listObjectsV1(
407+
String bucketName,
408+
@Nullable String prefix,
409+
@Nullable String delimiter,
410+
@Nullable String marker,
411+
@Nullable String encodingType,
412+
Integer maxKeys) {
401413

402414
if (maxKeys == 0) {
403415
return new ListBucketResult(List.of(), List.of(), null, encodingType,
@@ -470,11 +482,10 @@ public Map<String, String> bucketLocationHeaders(BucketMetadata bucketMetadata)
470482
}
471483

472484
public BucketMetadata verifyBucketExists(String bucketName) {
473-
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
474-
if (bucketMetadata == null) {
485+
if (!bucketStore.doesBucketExist(bucketName)) {
475486
throw NO_SUCH_BUCKET;
476487
} else {
477-
return bucketMetadata;
488+
return bucketStore.getBucketMetadata(bucketName);
478489
}
479490
}
480491

server/src/main/java/com/adobe/testing/s3mock/service/MultipartService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import java.util.Objects;
4949
import java.util.UUID;
5050
import java.util.stream.Collectors;
51+
import org.jspecify.annotations.Nullable;
5152
import org.slf4j.Logger;
5253
import org.slf4j.LoggerFactory;
5354
import org.springframework.http.HttpRange;
@@ -65,6 +66,7 @@ public MultipartService(BucketStore bucketStore, MultipartStore multipartStore)
6566
this.multipartStore = multipartStore;
6667
}
6768

69+
@Nullable
6870
public String putPart(
6971
String bucketName,
7072
String key,
@@ -81,6 +83,7 @@ public String putPart(
8183
path, encryptionHeaders);
8284
}
8385

86+
@Nullable
8487
public CopyPartResult copyPart(
8588
String bucketName,
8689
String key,
@@ -115,6 +118,7 @@ public CopyPartResult copyPart(
115118
}
116119
}
117120

121+
@Nullable
118122
public ListPartsResult getMultipartUploadParts(
119123
String bucketName,
120124
String key,
@@ -170,6 +174,7 @@ public void abortMultipartUpload(String bucketName, String key, String uploadId)
170174
}
171175
}
172176

177+
@Nullable
173178
public CompleteMultipartUploadResult completeMultipartUpload(
174179
String bucketName,
175180
String key,
@@ -237,7 +242,7 @@ public ListMultipartUploadsResult listMultipartUploads(
237242
String encodingType,
238243
String keyMarker,
239244
Integer maxUploads,
240-
String prefix,
245+
@Nullable String prefix,
241246
String uploadIdMarker
242247
) {
243248
String nextKeyMarker = null;

server/src/main/java/com/adobe/testing/s3mock/service/ObjectService.java

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.util.ArrayList;
5252
import java.util.List;
5353
import java.util.Map;
54+
import org.jspecify.annotations.Nullable;
5455
import org.slf4j.Logger;
5556
import org.slf4j.LoggerFactory;
5657

@@ -66,6 +67,7 @@ public ObjectService(BucketStore bucketStore, ObjectStore objectStore) {
6667
this.objectStore = objectStore;
6768
}
6869

70+
@Nullable
6971
public S3ObjectMetadata copyS3Object(String sourceBucketName,
7072
String sourceKey,
7173
String versionId,
@@ -148,7 +150,7 @@ public DeleteResult deleteObjects(String bucketName, Delete delete) {
148150
return response;
149151
}
150152

151-
public boolean deleteObject(String bucketName, String key, String versionId) {
153+
public boolean deleteObject(String bucketName, String key, @Nullable String versionId) {
152154
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
153155
var id = bucketMetadata.getID(key);
154156
if (id == null) {
@@ -162,31 +164,34 @@ public boolean deleteObject(String bucketName, String key, String versionId) {
162164
}
163165
}
164166

165-
public void setObjectTags(String bucketName, String key, String versionId, List<Tag> tags) {
167+
/**
168+
* <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions">API Reference</a>.
169+
*/
170+
public void setObjectTags(String bucketName, String key, @Nullable String versionId, @Nullable List<Tag> tags) {
166171
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
167172
var uuid = bucketMetadata.getID(key);
168173
objectStore.storeObjectTags(bucketMetadata, uuid, versionId, tags);
169174
}
170175

171-
public void setLegalHold(String bucketName, String key, String versionId, LegalHold legalHold) {
176+
public void setLegalHold(String bucketName, String key, @Nullable String versionId, LegalHold legalHold) {
172177
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
173178
var uuid = bucketMetadata.getID(key);
174179
objectStore.storeLegalHold(bucketMetadata, uuid, versionId, legalHold);
175180
}
176181

177-
public void setAcl(String bucketName, String key, String versionId, AccessControlPolicy policy) {
182+
public void setAcl(String bucketName, String key, @Nullable String versionId, AccessControlPolicy policy) {
178183
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
179184
var uuid = bucketMetadata.getID(key);
180185
objectStore.storeAcl(bucketMetadata, uuid, versionId, policy);
181186
}
182187

183-
public AccessControlPolicy getAcl(String bucketName, String key, String versionId) {
188+
public AccessControlPolicy getAcl(String bucketName, String key, @Nullable String versionId) {
184189
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
185190
var uuid = bucketMetadata.getID(key);
186191
return objectStore.readAcl(bucketMetadata, uuid, versionId);
187192
}
188193

189-
public void setRetention(String bucketName, String key, String versionId, Retention retention) {
194+
public void setRetention(String bucketName, String key, @Nullable String versionId, Retention retention) {
190195
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
191196
var uuid = bucketMetadata.getID(key);
192197
objectStore.storeRetention(bucketMetadata, uuid, versionId, retention);
@@ -209,7 +214,7 @@ public void verifyMd5(Path input, String contentMd5) {
209214
}
210215
}
211216

212-
public void verifyMd5(InputStream inputStream, String contentMd5) {
217+
public void verifyMd5(InputStream inputStream, @Nullable String contentMd5) {
213218
if (contentMd5 != null) {
214219
var md5 = DigestUtil.base64Digest(inputStream);
215220
if (!md5.equals(contentMd5)) {
@@ -255,9 +260,9 @@ public void verifyObjectMatching(
255260

256261
public void verifyObjectMatching(
257262
List<String> match,
258-
List<Instant> matchLastModifiedTime,
259-
List<Long> matchSize,
260-
S3ObjectMetadata s3ObjectMetadata) {
263+
@Nullable List<Instant> matchLastModifiedTime,
264+
@Nullable List<Long> matchSize,
265+
@Nullable S3ObjectMetadata s3ObjectMetadata) {
261266

262267
verifyObjectMatching(match, null, null, null, s3ObjectMetadata);
263268
if (s3ObjectMetadata != null) {
@@ -277,11 +282,11 @@ public void verifyObjectMatching(
277282
}
278283

279284
public void verifyObjectMatching(
280-
List<String> match,
281-
List<String> noneMatch,
282-
List<Instant> ifModifiedSince,
283-
List<Instant> ifUnmodifiedSince,
284-
S3ObjectMetadata s3ObjectMetadata) {
285+
@Nullable List<String> match,
286+
@Nullable List<String> noneMatch,
287+
@Nullable List<Instant> ifModifiedSince,
288+
@Nullable List<Instant> ifUnmodifiedSince,
289+
@Nullable S3ObjectMetadata s3ObjectMetadata) {
285290
if (s3ObjectMetadata == null) {
286291
// object does not exist, so we can skip the rest of the checks.
287292
return;
@@ -324,7 +329,7 @@ public void verifyObjectMatching(
324329
}
325330
}
326331

327-
public S3ObjectMetadata verifyObjectExists(String bucketName, String key, String versionId) {
332+
public S3ObjectMetadata verifyObjectExists(String bucketName, String key, @Nullable String versionId) {
328333
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
329334
var uuid = bucketMetadata.getID(key);
330335
if (uuid == null) {
@@ -339,7 +344,8 @@ public S3ObjectMetadata verifyObjectExists(String bucketName, String key, String
339344
return s3ObjectMetadata;
340345
}
341346

342-
public S3ObjectMetadata getObject(String bucketName, String key, String versionId) {
347+
@Nullable
348+
public S3ObjectMetadata getObject(String bucketName, String key, @Nullable String versionId) {
343349
var bucketMetadata = bucketStore.getBucketMetadata(bucketName);
344350
var uuid = bucketMetadata.getID(key);
345351
if (uuid == null) {
@@ -348,8 +354,7 @@ public S3ObjectMetadata getObject(String bucketName, String key, String versionI
348354
return objectStore.getS3ObjectMetadata(bucketMetadata, uuid, versionId);
349355
}
350356

351-
public S3ObjectMetadata verifyObjectLockConfiguration(String bucketName, String key,
352-
String versionId) {
357+
public S3ObjectMetadata verifyObjectLockConfiguration(String bucketName, String key, @Nullable String versionId) {
353358
var s3ObjectMetadata = verifyObjectExists(bucketName, key, versionId);
354359
var noLegalHold = s3ObjectMetadata.legalHold() == null;
355360
var noRetention = s3ObjectMetadata.retention() == null;

server/src/main/java/com/adobe/testing/s3mock/service/ServiceBase.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.function.Function;
3939
import java.util.function.UnaryOperator;
4040
import org.apache.commons.lang3.tuple.Pair;
41+
import org.jspecify.annotations.Nullable;
4142
import org.slf4j.Logger;
4243
import org.slf4j.LoggerFactory;
4344
import org.springframework.http.HttpHeaders;
@@ -51,7 +52,7 @@ public void verifyChecksum(Path path, String checksum, ChecksumAlgorithm checksu
5152
DigestUtil.verifyChecksum(checksum, checksumFor, checksumAlgorithm);
5253
}
5354

54-
public Pair<Path, String> toTempFile(InputStream inputStream, HttpHeaders httpHeaders) {
55+
public Pair<Path, @Nullable String> toTempFile(InputStream inputStream, HttpHeaders httpHeaders) {
5556
try {
5657
var tempFile = Files.createTempFile("ObjectService", "toTempFile");
5758
try (var os = Files.newOutputStream(tempFile);
@@ -70,7 +71,7 @@ public Pair<Path, String> toTempFile(InputStream inputStream, HttpHeaders httpHe
7071
}
7172
}
7273

73-
public Pair<Path, String> toTempFile(InputStream inputStream) {
74+
public Pair<Path, @Nullable String> toTempFile(InputStream inputStream) {
7475
try {
7576
var tempFile = Files.createTempFile("ObjectService", "toTempFile");
7677
try (var os = Files.newOutputStream(tempFile)) {
@@ -96,7 +97,7 @@ static <T> List<T> mapContents(
9697
static <T> List<T> filterBy(
9798
List<T> contents,
9899
Function<T, String> function,
99-
String compareTo
100+
@Nullable String compareTo
100101
) {
101102
if (isNotEmpty(compareTo)) {
102103
return contents
@@ -111,7 +112,7 @@ static <T> List<T> filterBy(
111112
static <T> List<T> filterBy(
112113
List<T> contents,
113114
Function<T, Integer> function,
114-
Integer compareTo
115+
@Nullable Integer compareTo
115116
) {
116117
if (compareTo != null) {
117118
return contents
@@ -126,7 +127,7 @@ static <T> List<T> filterBy(
126127
static <T> List<T> filterBy(
127128
List<T> contents,
128129
Function<T, String> function,
129-
List<String> prefixes
130+
@Nullable List<String> prefixes
130131
) {
131132
if (prefixes != null && !prefixes.isEmpty()) {
132133
return contents
@@ -154,8 +155,8 @@ static <T> List<T> filterBy(
154155
* @param function the function to apply on a content to extract the value to collapse the prefixes on
155156
*/
156157
static <T> List<String> collapseCommonPrefixes(
157-
String queryPrefix,
158-
String delimiter,
158+
@Nullable String queryPrefix,
159+
@Nullable String delimiter,
159160
List<T> contents,
160161
Function<T, String> function
161162
) {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2017-2025 Adobe.
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+
* http://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+
@NullMarked
18+
package com.adobe.testing.s3mock.service;
19+
20+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)