Skip to content

Commit e263348

Browse files
authored
Migrate commons/observation modules to jSpecify (#6158)
Replaces our JSR-305-based nullability annotations with the jSpecify annotations and adds NullAway compile-time analysis of nullability.
1 parent d9eb315 commit e263348

File tree

49 files changed

+259
-175
lines changed

Some content is hidden

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

49 files changed

+259
-175
lines changed

build.gradle

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,38 +73,52 @@ subprojects {
7373

7474
if (project.name != 'micrometer-bom') {
7575
tasks.withType(JavaCompile).configureEach {
76-
options.errorprone.disableWarningsInGeneratedCode = true
77-
options.errorprone.excludedPaths = ".*/build/generated/.*"
78-
options.errorprone.error(
79-
"BadImport",
80-
"DefaultCharset",
81-
"LongDoubleConversion",
82-
"MissingOverride",
83-
"StringCaseLocaleUsage"
84-
)
85-
options.errorprone.disable(
86-
"StringConcatToTextBlock" // Requires JDK 15+
87-
)
88-
8976
if (it.name == "compileJava" && !(it.project.name in ["micrometer-java11"])) {
9077
options.errorprone.disable(
9178
"JavaDurationGetSecondsToToSeconds" // Requires JDK 9+
9279
)
9380
}
81+
options.errorprone {
82+
disableWarningsInGeneratedCode = true
83+
excludedPaths = ".*/build/generated/.*"
9484

95-
if (!javaLanguageVersion.canCompileOrRun(17)) {
96-
// Error Prone does not work with JDK <17
97-
options.errorprone.enabled = false
98-
}
99-
if (System.env.CI != null) {
100-
options.errorprone.disableAllWarnings = true
85+
disable(
86+
"StringConcatToTextBlock" // Requires JDK 15+
87+
)
88+
89+
error("BadImport",
90+
"DefaultCharset",
91+
"LongDoubleConversion",
92+
"MissingOverride",
93+
"NullAway",
94+
"StringCaseLocaleUsage"
95+
)
96+
97+
option("NullAway:OnlyNullMarked", "true")
98+
option("NullAway:CustomContractAnnotations", "io.micrometer.common.lang.internal.Contract")
99+
option("NullAway:CheckContracts", "true")
100+
option("NullAway:HandleTestAssertionLibraries", "true")
101+
if (javaLanguageVersion.canCompileOrRun(22)) {
102+
// see https://bugs.openjdk.org/browse/JDK-8346471
103+
// see https://github.com/uber/NullAway/wiki/JSpecify-Support
104+
option("NullAway:JSpecifyMode", "true")
105+
}
106+
if (!javaLanguageVersion.canCompileOrRun(17)) {
107+
// Error Prone does not work with JDK <17
108+
enabled = false
109+
}
110+
if (System.env.CI != null) {
111+
disableAllWarnings = true
112+
}
101113
}
102114
}
103115
if ((project.name.contains('samples') && !project.name.contains('kotlin')) || project.name.contains('benchmarks')) {
104116
apply plugin: 'java'
105117
} else {
106118
apply plugin: 'java-library'
107119
dependencies {
120+
api(libs.jspecify)
121+
108122
testImplementation platform(libs.junitBom)
109123
testImplementation libs.junitJupiter
110124
testRuntimeOnly libs.junitPlatformLauncher
@@ -139,6 +153,7 @@ subprojects {
139153
optionalApi libs.jsr305
140154
checkstyle libs.spring.javaformatCheckstyle
141155
errorprone(libs.errorprone)
156+
errorprone(libs.nullAway)
142157
}
143158

144159
tasks {

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ jmhAnnotationProcessor = { module = "org.openjdk.jmh:jmh-generator-annprocess",
156156
jooq = { module = "org.jooq:jooq", version = "3.14.16" }
157157
jooqLatest = { module = "org.jooq:jooq", version.ref = "jooqNew" }
158158
jsonPath = { module = "com.jayway.jsonpath:json-path", version = "2.9.0" }
159+
jspecify = { module = "org.jspecify:jspecify", version = "1.0.0" }
159160
jsr107 = { module = "org.jsr107.ri:cache-ri-impl", version.ref = "jsr107" }
160161
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
161162
junitBom = { module = "org.junit:junit-bom", version.ref = "junit" }
@@ -179,6 +180,7 @@ mockitoCore5 = { module = "org.mockito:mockito-core", version.ref = "mockito5" }
179180
mongoSync = { module = "org.mongodb:mongodb-driver-sync", version.ref = "mongo" }
180181
nettyBom = { module = "io.netty:netty-bom", version.ref = "netty" }
181182
newrelicApi = { module = "com.newrelic.agent.java:newrelic-api", version.ref = "newrelic-api" }
183+
nullAway = { module = "com.uber.nullaway:nullaway", version = "0.12.7" }
182184
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
183185
# some proto are marked alpha, hence the alpha version. metrics proto is what we use and it is marked stable
184186
openTelemetry-proto = { module = "io.opentelemetry.proto:opentelemetry-proto", version = "1.5.0-alpha" }

micrometer-commons/src/main/java/io/micrometer/common/ImmutableKeyValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package io.micrometer.common;
1717

18-
import io.micrometer.common.lang.Nullable;
18+
import org.jspecify.annotations.Nullable;
1919

2020
import java.util.Objects;
2121

micrometer-commons/src/main/java/io/micrometer/common/KeyValues.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
*/
1616
package io.micrometer.common;
1717

18-
import io.micrometer.common.lang.Nullable;
18+
import io.micrometer.common.lang.internal.Contract;
19+
import org.jspecify.annotations.Nullable;
1920

2021
import java.util.*;
2122
import java.util.function.Function;
@@ -192,8 +193,8 @@ public KeyValues and(String key, String value) {
192193
* @param keyValues the key/value pairs to add, elements mustn't be null
193194
* @return a new {@code KeyValues} instance
194195
*/
195-
public KeyValues and(@Nullable String... keyValues) {
196-
if (blankVarargs(keyValues)) {
196+
public KeyValues and(String @Nullable... keyValues) {
197+
if (isEmptyVarargs(keyValues)) {
197198
return this;
198199
}
199200
return and(KeyValues.of(keyValues));
@@ -205,8 +206,8 @@ public KeyValues and(@Nullable String... keyValues) {
205206
* @param keyValues the key values to add, elements mustn't be null
206207
* @return a new {@code KeyValues} instance
207208
*/
208-
public KeyValues and(@Nullable KeyValue... keyValues) {
209-
if (blankVarargs(keyValues)) {
209+
public KeyValues and(KeyValue @Nullable... keyValues) {
210+
if (isEmptyVarargs(keyValues)) {
210211
return this;
211212
}
212213
return and(toKeyValues(keyValues));
@@ -339,7 +340,7 @@ public static KeyValues concat(@Nullable Iterable<? extends KeyValue> keyValues,
339340
* @return the merged key values
340341
*/
341342
public static KeyValues concat(@Nullable Iterable<? extends KeyValue> keyValues,
342-
@Nullable String... otherKeyValues) {
343+
String @Nullable... otherKeyValues) {
343344
return KeyValues.of(keyValues).and(otherKeyValues);
344345
}
345346

@@ -395,8 +396,8 @@ public static KeyValues of(String key, String value) {
395396
* @param keyValues the key/value pairs to add, elements mustn't be null
396397
* @return a new {@code KeyValues} instance
397398
*/
398-
public static KeyValues of(@Nullable String... keyValues) {
399-
if (blankVarargs(keyValues)) {
399+
public static KeyValues of(String @Nullable... keyValues) {
400+
if (isEmptyVarargs(keyValues)) {
400401
return empty();
401402
}
402403
if (keyValues.length % 2 == 1) {
@@ -409,7 +410,8 @@ public static KeyValues of(@Nullable String... keyValues) {
409410
return toKeyValues(keyValueArray);
410411
}
411412

412-
private static boolean blankVarargs(@Nullable Object[] args) {
413+
@Contract("null -> true")
414+
private static boolean isEmptyVarargs(@Nullable Object @Nullable [] args) {
413415
return args == null || args.length == 0 || (args.length == 1 && args[0] == null);
414416
}
415417

@@ -419,7 +421,7 @@ private static boolean blankVarargs(@Nullable Object[] args) {
419421
* @param keyValues the key values to add, elements mustn't be null
420422
* @return a new {@code KeyValues} instance
421423
*/
422-
public static KeyValues of(@Nullable KeyValue... keyValues) {
424+
public static KeyValues of(KeyValue @Nullable... keyValues) {
423425
return empty().and(keyValues);
424426
}
425427

micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotatedObject.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package io.micrometer.common.annotation;
1717

18-
import io.micrometer.common.lang.Nullable;
18+
import org.jspecify.annotations.Nullable;
1919

2020
import java.lang.annotation.Annotation;
2121

@@ -31,8 +31,7 @@ class AnnotatedObject {
3131

3232
final Annotation annotation;
3333

34-
@Nullable
35-
final Object object;
34+
final @Nullable Object object;
3635

3736
AnnotatedObject(Annotation annotation, @Nullable Object object) {
3837
this.annotation = annotation;

micrometer-commons/src/main/java/io/micrometer/common/annotation/AnnotationHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
package io.micrometer.common.annotation;
1717

1818
import io.micrometer.common.KeyValue;
19-
import io.micrometer.common.lang.Nullable;
2019
import io.micrometer.common.util.internal.logging.InternalLogger;
2120
import io.micrometer.common.util.internal.logging.InternalLoggerFactory;
2221
import org.aspectj.lang.ProceedingJoinPoint;
2322
import org.aspectj.lang.reflect.MethodSignature;
23+
import org.jspecify.annotations.Nullable;
2424

2525
import java.lang.annotation.Annotation;
2626
import java.lang.reflect.Method;

micrometer-commons/src/main/java/io/micrometer/common/annotation/ValueExpressionResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
package io.micrometer.common.annotation;
1717

1818
import io.micrometer.common.KeyValue;
19-
import io.micrometer.common.lang.Nullable;
19+
import org.jspecify.annotations.Nullable;
2020

2121
/**
2222
* Resolves the {@link KeyValue} value for the given parameter and the provided
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2025 VMware, Inc.
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+
package io.micrometer.common.lang.internal;
17+
18+
import java.lang.annotation.*;
19+
20+
/**
21+
* This is for internal use only.
22+
*/
23+
@Documented
24+
@Retention(RetentionPolicy.CLASS)
25+
@Target(ElementType.METHOD)
26+
public @interface Contract {
27+
28+
String value() default "";
29+
30+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2025 VMware, Inc.
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+
@NullMarked
17+
package io.micrometer.common.lang.internal;
18+
19+
import org.jspecify.annotations.NullMarked;

micrometer-commons/src/main/java/io/micrometer/common/lang/package-info.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
@NonNullApi
17-
@NonNullFields
16+
@NullMarked
1817
package io.micrometer.common.lang;
18+
19+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)