Skip to content

Commit cba4159

Browse files
authored
feat(spawn-jdk): add AddExports, AddOpens, AddReads, and PackageName option types (#31)
1 parent 04b75f4 commit cba4159

10 files changed

Lines changed: 810 additions & 11 deletions

File tree

TODO.md

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
# NOW
33
------------------------------------------------------------------------------------------------------------------------
44

5-
* Refactor/Replace Docker okhttp client with Java HTTP Client
6-
75
* Introduce Debugging support
86

97
* TODO: Create issues for the remaining JDK features/options (eg: JMX, Garbage Collection, Logging, Flight Recorder, Early Access, Preview etc)
@@ -17,15 +15,6 @@
1715

1816
* Introduce Composition Tests
1917

20-
* FIX! Introduce AddReads option to support representing "--add-reads" (and inheritance)
21-
* FIX! Introduce AddOpens option to support representing "--add-opens" (and inheritance)
22-
23-
eg: to mimic this stuff that's added automatically!
24-
25-
// JDKOption.of("--add-reads"), JDKOption.of("build.spawn.platform.local.jdk=ALL-UNNAMED"),
26-
// JDKOption.of("--add-opens"),
27-
// JDKOption.of("build.spawn.platform.local.jdk/build.spawn.platform.local.jdk=ALL-UNNAMED"),
28-
2918
------------------------------------------------------------------------------------------------------------------------
3019
# LATER
3120
------------------------------------------------------------------------------------------------------------------------

spawn-jdk/src/main/java/build/spawn/jdk/JDKApplication.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
import build.spawn.application.Platform;
3131
import build.spawn.application.option.ApplicationSubscriber;
3232
import build.spawn.application.option.LaunchIdentity;
33+
import build.spawn.jdk.option.AddExports;
3334
import build.spawn.jdk.option.AddModules;
35+
import build.spawn.jdk.option.AddOpens;
36+
import build.spawn.jdk.option.AddReads;
3437
import build.spawn.jdk.option.ClassPath;
3538
import build.spawn.jdk.option.Headless;
3639
import build.spawn.jdk.option.Jar;
@@ -112,6 +115,15 @@ public void onPreparing(final Platform platform,
112115

113116
PatchModule.detect()
114117
.forEach(options::add);
118+
119+
AddExports.detect()
120+
.forEach(options::add);
121+
122+
AddOpens.detect()
123+
.forEach(options::add);
124+
125+
AddReads.detect()
126+
.forEach(options::add);
115127
}
116128
}
117129

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package build.spawn.jdk.option;
2+
3+
/*-
4+
* #%L
5+
* Spawn JDK
6+
* %%
7+
* Copyright (C) 2026 Workday, Inc.
8+
* %%
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* #L%
21+
*/
22+
23+
import build.base.configuration.CollectedOption;
24+
import build.base.configuration.ConfigurationBuilder;
25+
import build.base.option.JDKVersion;
26+
import build.base.table.Cell;
27+
import build.base.table.Table;
28+
import build.base.table.Tabular;
29+
import build.base.table.option.CellSeparator;
30+
import build.base.table.option.TableName;
31+
import build.spawn.application.Platform;
32+
33+
import java.lang.management.ManagementFactory;
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
import java.util.Objects;
37+
import java.util.Optional;
38+
import java.util.function.Supplier;
39+
import java.util.stream.Stream;
40+
41+
/**
42+
* A {@link JDKOption} to specify a {@code --add-exports=source-module/package=target-module}.
43+
*
44+
* @author reed.vonredwitz
45+
* @since May-2026
46+
*/
47+
public class AddExports
48+
implements JDKOption, CollectedOption<List>, Tabular {
49+
50+
/**
51+
* The source {@link ModuleName} that owns the package.
52+
*/
53+
private final ModuleName sourceModule;
54+
55+
/**
56+
* The {@link PackageName} to export.
57+
*/
58+
private final PackageName packageName;
59+
60+
/**
61+
* The target {@link ModuleName} to export to.
62+
*/
63+
private final ModuleName targetModule;
64+
65+
/**
66+
* Constructs an {@link AddExports}.
67+
*
68+
* @param sourceModule the source {@link ModuleName}
69+
* @param packageName the {@link PackageName}
70+
* @param targetModule the target {@link ModuleName}
71+
*/
72+
private AddExports(final ModuleName sourceModule,
73+
final PackageName packageName,
74+
final ModuleName targetModule) {
75+
this.sourceModule = Objects.requireNonNull(sourceModule, "The source ModuleName must not be null");
76+
this.packageName = Objects.requireNonNull(packageName, "The PackageName must not be null");
77+
this.targetModule = Objects.requireNonNull(targetModule, "The target ModuleName must not be null");
78+
}
79+
80+
@Override
81+
public boolean isSupported(final JDKVersion jdkVersion,
82+
final ConfigurationBuilder options) {
83+
84+
return jdkVersion.isModular();
85+
}
86+
87+
@Override
88+
public Stream<String> resolve(final Platform platform,
89+
final ConfigurationBuilder options) {
90+
91+
final var arguments = new ArrayList<String>(2);
92+
93+
arguments.add("--add-exports");
94+
arguments.add(this.sourceModule.get() + "/" + this.packageName.get() + "=" + this.targetModule.get());
95+
96+
return arguments.stream();
97+
}
98+
99+
@Override
100+
public Optional<Supplier<Table>> getTableSupplier() {
101+
return Optional.of(() -> {
102+
final Table table = Table.create();
103+
table.options().add(TableName.of("Add Exports"));
104+
table.options().add(CellSeparator.of("="));
105+
return table;
106+
});
107+
}
108+
109+
@Override
110+
public void tabularize(final Table table) {
111+
table.addRow(
112+
Cell.of(this.sourceModule.get() + "/" + this.packageName.get()),
113+
Cell.of(this.targetModule.get()));
114+
}
115+
116+
@Override
117+
public boolean equals(final Object object) {
118+
if (this == object) {
119+
return true;
120+
}
121+
if (!(object instanceof final AddExports that)) {
122+
return false;
123+
}
124+
return Objects.equals(this.sourceModule, that.sourceModule)
125+
&& Objects.equals(this.packageName, that.packageName)
126+
&& Objects.equals(this.targetModule, that.targetModule);
127+
}
128+
129+
@Override
130+
public int hashCode() {
131+
return Objects.hash(this.sourceModule, this.packageName, this.targetModule);
132+
}
133+
134+
/**
135+
* Creates an {@link AddExports}.
136+
*
137+
* @param sourceModule the source {@link ModuleName}
138+
* @param packageName the {@link PackageName}
139+
* @param targetModule the target {@link ModuleName}
140+
* @return a new {@link AddExports}
141+
*/
142+
public static AddExports of(final ModuleName sourceModule,
143+
final PackageName packageName,
144+
final ModuleName targetModule) {
145+
return new AddExports(sourceModule, packageName, targetModule);
146+
}
147+
148+
/**
149+
* Creates an {@link AddExports}.
150+
*
151+
* @param sourceModule the source module name
152+
* @param packageName the package name
153+
* @param targetModule the target module name
154+
* @return a new {@link AddExports}
155+
*/
156+
public static AddExports of(final String sourceModule,
157+
final String packageName,
158+
final String targetModule) {
159+
return new AddExports(ModuleName.of(sourceModule), PackageName.of(packageName), ModuleName.of(targetModule));
160+
}
161+
162+
/**
163+
* Detects the {@link Stream} of {@link AddExports} directives for this Virtual Machine.
164+
*
165+
* @return the {@link Stream} of {@link AddExports}
166+
*/
167+
public static Stream<AddExports> detect() {
168+
return ManagementFactory.getRuntimeMXBean()
169+
.getInputArguments()
170+
.stream()
171+
.filter(argument -> argument.startsWith("--add-exports="))
172+
.map(argument -> argument.substring("--add-exports=".length()))
173+
.map(value -> value.split("=", 2))
174+
.map(parts -> {
175+
final var moduleAndPackage = parts[0].split("/", 2);
176+
return AddExports.of(moduleAndPackage[0], moduleAndPackage[1], parts[1]);
177+
});
178+
}
179+
}

0 commit comments

Comments
 (0)