Skip to content

Commit 0cdfaba

Browse files
Breusgavlyukovskiy
andauthored
Add support for Java 11 and 17 simultaneously using multi-release JAR (#214)
Fixes #212 Co-authored-by: Artur Havliukovskyi <agavlyukovskiy@gmail.com>
1 parent 7eb9a97 commit 0cdfaba

21 files changed

+902
-127
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ The package requires no additional runtime dependencies.
7575

7676
## JDK Compatibility
7777

78-
The `json-masker` baseline JDK requirement is JDK 17. However, we might consider releasing a version which lowers this
79-
requirement to JDK 11, when requested.
78+
From version 1.1 onwards, the `json-masker` artifact became a multi-release JAR (MRJAR) supporting both JDK 11 and
79+
JDK 17 simultaneously, so **the minimum JDK requirement is JDK 11**.
8080

8181
## Usage examples
8282

SECURITY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Security Policy
22

3-
Last Updated: 25-03-2024
3+
Last Updated: 26-01-2025
44

55
## Supported Versions
66

@@ -9,6 +9,7 @@ The following versions of the library are supported for security updates.
99
| Version | Supported |
1010
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|
1111
| [![Maven Central](https://img.shields.io/maven-metadata/v.svg?metadataUrl=https%3A%2F%2Frepo1.maven.org%2Fmaven2%2Fdev%2Fblaauwendraad%2Fjson-masker%2Fmaven-metadata.xml&label=&color=green&style=flat-square)](https://central.sonatype.com/artifact/dev.blaauwendraad/json-masker) _(latest)_ | :white_check_mark: |
12+
| **1.0.x** | :x: |
1213
| **1.0.0-rcX** | :x: |
1314
| **0.2.x** | :x: |
1415
| **0.1.x** | :x: |

build.gradle.kts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
alias(libs.plugins.nexus.publish)
99
alias(libs.plugins.jmh)
1010
alias(libs.plugins.errorprone)
11+
alias(libs.plugins.mrjar)
1112
`maven-publish`
1213
`java-library`
1314
signing
@@ -31,13 +32,15 @@ java {
3132
}
3233
withJavadocJar()
3334
withSourcesJar()
34-
registerFeature("nullabilityAnnotations") {
35-
usingSourceSet(sourceSets["main"])
36-
}
35+
}
36+
37+
multiRelease {
38+
targetVersions(11, 17)
3739
}
3840

3941
dependencies {
40-
"nullabilityAnnotationsImplementation"(libs.jspecify)
42+
api(libs.jspecify)
43+
"java17Implementation"(libs.jspecify)
4144

4245
testImplementation(libs.assertj.core)
4346
testImplementation(libs.jackson.databind)
@@ -89,7 +92,7 @@ publishing {
8992
}
9093
developer {
9194
id = "gavlyukovskiy"
92-
name = "Arthur Gavlyukovskiy"
95+
name = "Artur Havliukovskyi"
9396
email = "agavlyukovskiy@gmail.com"
9497
}
9598
}
@@ -161,6 +164,16 @@ sonar {
161164
}
162165

163166
tasks {
167+
compileJava {
168+
sourceCompatibility = "11"
169+
targetCompatibility = "11"
170+
}
171+
172+
compileTestJava {
173+
sourceCompatibility = "17"
174+
targetCompatibility = "17"
175+
}
176+
164177
test {
165178
useJUnitPlatform()
166179
}

gradle/libs.versions.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ jmh = { id = "me.champeau.jmh", version = "0.7.2" }
1919
nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
2020
test-logger = { id = "com.adarshr.test-logger", version = "4.0.0" }
2121
sonarqube = { id = "org.sonarqube", version = "6.0.1.5171" }
22-
errorprone = { id = "net.ltgt.errorprone", version = "4.1.0" }
22+
errorprone = { id = "net.ltgt.errorprone", version = "4.1.0" }
23+
mrjar = { id = "me.champeau.mrjar", version = "0.1.1" }

src/main/java/dev/blaauwendraad/masker/json/BufferedMaskingState.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ private void moveCurrentTokenToBeginningOfBuffer(int currentTokenLength) {
120120
bufferSize <<= 1; // note: <<= 1 is equal to doubling the bufferSize
121121
if (bufferSize > MAX_BUFFER_SIZE) {
122122
throw new InvalidJsonException(
123-
"Invalid JSON input provided: it contains a single JSON token (key or value) with %s characters".formatted(
124-
currentTokenLength));
123+
String.format("Invalid JSON input provided: it contains a single JSON token (key or value) with %s characters", currentTokenLength));
125124
}
126125
byte[] extendedBuffer = new byte[bufferSize];
127126

src/main/java/dev/blaauwendraad/masker/json/KeyContainsMasker.java

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private void mask(MaskingState maskingState) {
8282
}
8383
}
8484
} catch (ArrayIndexOutOfBoundsException | StackOverflowError e) {
85-
throw new InvalidJsonException("Invalid JSON input provided: %s".formatted(e.getMessage()), e);
85+
throw new InvalidJsonException(String.format("Invalid JSON input provided: %s", e.getMessage()), e);
8686
}
8787
}
8888

@@ -101,40 +101,55 @@ private boolean visitValue(MaskingState maskingState, @Nullable JsonPathTracker
101101
}
102102
// using switch-case over 'if'-statements to improve performance by ~20% (measured in benchmarks)
103103
switch (maskingState.byteAtCurrentIndex()) {
104-
case '[' -> visitArray(maskingState, jsonPathTracker, keyMaskingConfig);
105-
case '{' -> visitObject(maskingState, jsonPathTracker, keyMaskingConfig);
106-
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> {
104+
case '[':
105+
visitArray(maskingState, jsonPathTracker, keyMaskingConfig);
106+
break;
107+
case '{':
108+
visitObject(maskingState, jsonPathTracker, keyMaskingConfig);
109+
break;
110+
case '-':
111+
case '0':
112+
case '1':
113+
case '2':
114+
case '3':
115+
case '4':
116+
case '5':
117+
case '6':
118+
case '7':
119+
case '8':
120+
case '9':
107121
if (keyMaskingConfig != null) {
108122
maskNumber(maskingState, keyMaskingConfig);
109123
} else {
110124
stepOverNumericValue(maskingState);
111125
}
112-
}
113-
case '"' -> {
126+
break;
127+
case '"':
114128
if (keyMaskingConfig != null) {
115129
maskString(maskingState, keyMaskingConfig);
116130
} else {
117131
stepOverStringValue(maskingState);
118132
}
119-
}
120-
case 't' -> {
133+
break;
134+
case 't':
121135
if (keyMaskingConfig != null) {
122136
maskBoolean(maskingState, keyMaskingConfig);
123137
} else {
124138
maskingState.incrementIndex(4);
125139
}
126-
}
127-
case 'f' -> {
140+
break;
141+
case 'f':
128142
if (keyMaskingConfig != null) {
129143
maskBoolean(maskingState, keyMaskingConfig);
130144
} else {
131145
maskingState.incrementIndex(5);
132146
}
133-
}
134-
case 'n' -> maskingState.incrementIndex(4);
135-
default -> {
147+
break;
148+
case 'n':
149+
maskingState.incrementIndex(4);
150+
break;
151+
default:
136152
return false;
137-
}
138153
}
139154
return true;
140155
}
@@ -313,13 +328,39 @@ private void maskBoolean(MaskingState maskingState, KeyMaskingConfig keyMaskingC
313328
*/
314329
private static void stepOverValue(MaskingState maskingState) {
315330
switch (maskingState.byteAtCurrentIndex()) {
316-
case '"' -> stepOverStringValue(maskingState);
317-
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> stepOverNumericValue(maskingState);
318-
case 't', 'n' -> maskingState.incrementIndex(4); // true or null
319-
case 'f' -> maskingState.incrementIndex(5); // false
320-
case '{' -> stepOverObject(maskingState);
321-
case '[' -> stepOverArray(maskingState);
322-
default -> { /* return */ }
331+
case '"':
332+
stepOverStringValue(maskingState);
333+
break;
334+
case '-':
335+
case '0':
336+
case '1':
337+
case '2':
338+
case '3':
339+
case '4':
340+
case '5':
341+
case '6':
342+
case '7':
343+
case '8':
344+
case '9':
345+
stepOverNumericValue(maskingState);
346+
break;
347+
case 't':
348+
case 'n':
349+
maskingState.incrementIndex(4);
350+
break;
351+
// true or null
352+
case 'f':
353+
maskingState.incrementIndex(5);
354+
break;
355+
// false
356+
case '{':
357+
stepOverObject(maskingState);
358+
break;
359+
case '[':
360+
stepOverArray(maskingState);
361+
break;
362+
default:/* return */
363+
break;
323364
}
324365
}
325366

src/main/java/dev/blaauwendraad/masker/json/KeyMatcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ RadixTriePointer checkpoint() {
667667
*/
668668
@Override
669669
public String toString() {
670-
return "[sequence: %s] %s".formatted(currentMatchingNodePrefixIndex, currentMatchingNode);
670+
return String.format("[sequence: %s] %s", currentMatchingNodePrefixIndex, currentMatchingNode);
671671
}
672672
}
673673
}

src/main/java/dev/blaauwendraad/masker/json/MaskingState.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public String asString(int fromIndex, int length) {
265265
@Override
266266
public InvalidJsonException invalidJson(String message, int index) {
267267
int offset = getCurrentTokenStartIndex();
268-
return new InvalidJsonException("%s at index %s".formatted(message, offset + index));
268+
return new InvalidJsonException(String.format("%s at index %s", message, offset + index));
269269
}
270270

271271
private void checkCurrentValueBounds(int index) {
@@ -297,16 +297,27 @@ public String toString() {
297297
* every mask, we store the replacement operations in a list and apply them all at once at the end, thus making only
298298
* a single resize operation.
299299
*
300-
* @param startIndex index from which to start replacing
301-
* @param length the length of the target value slice
302-
* @param mask byte array mask to use as replacement for the value
303-
* @param maskRepeat number of times to repeat the mask (for cases when every character or digit is masked)
304-
*
305300
* @see #flushReplacementOperations()
306301
*/
307-
@SuppressWarnings({"ArrayRecordComponent", "java:S6218"}) // never used for comparison
308-
private record ReplacementOperation(int startIndex, int length, byte[] mask, int maskRepeat) {
302+
private static final class ReplacementOperation {
303+
private final int startIndex;
304+
private final int length;
305+
private final byte[] mask;
306+
private final int maskRepeat;
309307

308+
/**
309+
* @param startIndex index from which to start replacing
310+
* @param length the length of the target value slice
311+
* @param mask byte array mask to use as replacement for the value
312+
* @param maskRepeat number of times to repeat the mask (for cases when every character or digit is masked)
313+
*/
314+
private ReplacementOperation(int startIndex, int length, byte[] mask, int maskRepeat) {
315+
this.startIndex = startIndex;
316+
this.length = length;
317+
this.mask = mask;
318+
this.maskRepeat = maskRepeat;
319+
}
320+
310321
/**
311322
* The difference between the mask length and the length of the target value to replace.
312323
* Used to calculate keep track of the offset during replacements.
@@ -315,5 +326,4 @@ public int difference() {
315326
return mask.length * maskRepeat - length;
316327
}
317328
}
318-
319329
}

src/main/java/dev/blaauwendraad/masker/json/ValueMasker.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@
1414
*
1515
* @see ValueMaskers for several out-of-the-box implementations
1616
*/
17-
public sealed interface ValueMasker permits
18-
ValueMasker.StringMasker,
19-
ValueMasker.NumberMasker,
20-
ValueMasker.BooleanMasker,
21-
ValueMasker.AnyValueMasker {
17+
// Sealed interface from Java 17 onwards, reverted back to non-sealed to Java 11 - Java 16 release
18+
public /* sealed */ interface ValueMasker {
2219
/**
2320
* Used for masking JSON values. Accepts {@link ValueMaskerContext} that contains context of the
2421
* current value being masked.
@@ -31,7 +28,7 @@ public sealed interface ValueMasker permits
3128
* @see dev.blaauwendraad.masker.json.config.KeyMaskingConfig.Builder#maskStringsWith(ValueMasker.StringMasker)
3229
*/
3330
@FunctionalInterface
34-
non-sealed interface StringMasker extends ValueMasker {
31+
interface StringMasker extends ValueMasker {
3532
}
3633

3734
/**
@@ -40,7 +37,7 @@ non-sealed interface StringMasker extends ValueMasker {
4037
* @see dev.blaauwendraad.masker.json.config.KeyMaskingConfig.Builder#maskNumbersWith(ValueMasker.NumberMasker)
4138
*/
4239
@FunctionalInterface
43-
non-sealed interface NumberMasker extends ValueMasker {
40+
interface NumberMasker extends ValueMasker {
4441
}
4542

4643
/**
@@ -49,13 +46,13 @@ non-sealed interface NumberMasker extends ValueMasker {
4946
* @see dev.blaauwendraad.masker.json.config.KeyMaskingConfig.Builder#maskBooleansWith(ValueMasker.BooleanMasker)
5047
*/
5148
@FunctionalInterface
52-
non-sealed interface BooleanMasker extends ValueMasker {
49+
interface BooleanMasker extends ValueMasker {
5350
}
5451

5552
/**
5653
* {@link ValueMasker} that can mask any JSON value (string, number or a boolean).
5754
*/
5855
@FunctionalInterface
59-
non-sealed interface AnyValueMasker extends ValueMasker, StringMasker, NumberMasker, BooleanMasker {
56+
interface AnyValueMasker extends ValueMasker, StringMasker, NumberMasker, BooleanMasker {
6057
}
6158
}

0 commit comments

Comments
 (0)