Skip to content

Commit f1fcd32

Browse files
committed
Add a new 'mainHeadless' field to the plugin manifest to allow plugins with a UI to provide an alternate implementation that doesn't require JavaFX.
1 parent 8e881d2 commit f1fcd32

File tree

6 files changed

+64
-23
lines changed

6 files changed

+64
-23
lines changed

chunky/src/java/se/llbit/chunky/plugin/loader/JarPluginLoader.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@
1616
@PluginApi
1717
public class JarPluginLoader implements PluginLoader {
1818
public void load(BiConsumer<Plugin, PluginManifest> onLoad, PluginManifest pluginManifest) {
19+
load(onLoad, pluginManifest, null);
20+
}
21+
22+
@Override
23+
public void load(BiConsumer<Plugin, PluginManifest> onLoad, PluginManifest pluginManifest, PluginLoaderContext context) {
24+
boolean headless = context != null && context.isHeadless();
1925
try {
20-
Class<?> pluginClass = loadPluginClass(pluginManifest.main, pluginManifest.pluginJar);
26+
Class<?> pluginClass = loadPluginClass(headless && !pluginManifest.mainHeadless.isEmpty() ? pluginManifest.mainHeadless : pluginManifest.main, pluginManifest.pluginJar);
2127
Plugin plugin = (Plugin) pluginClass.getDeclaredConstructor().newInstance();
2228
onLoad.accept(plugin, pluginManifest);
2329
} catch (IOException | ClassNotFoundException e) {
@@ -33,13 +39,13 @@ public void load(BiConsumer<Plugin, PluginManifest> onLoad, PluginManifest plugi
3339
* This method is {@link PluginApi} to allow plugins to override only classloading functionality of the default plugin loader.
3440
*
3541
* @param pluginMainClass The plugin's main class to load.
36-
* @param pluginJarFile The jar file to load classes from.
42+
* @param pluginJarFile The jar file to load classes from.
3743
* @return The loaded plugin's main class
3844
* @throws ClassNotFoundException If the main class doesn't exist
39-
* @throws MalformedURLException If the jar file cannot be converted to a URL
45+
* @throws MalformedURLException If the jar file cannot be converted to a URL
4046
*/
4147
@PluginApi
4248
protected Class<?> loadPluginClass(String pluginMainClass, File pluginJarFile) throws ClassNotFoundException, MalformedURLException {
43-
return new URLClassLoader(new URL[] { pluginJarFile.toURI().toURL() }).loadClass(pluginMainClass);
49+
return new URLClassLoader(new URL[]{pluginJarFile.toURI().toURL()}).loadClass(pluginMainClass);
4450
}
4551
}

chunky/src/java/se/llbit/chunky/plugin/loader/PluginLoader.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@ public interface PluginLoader {
1515
*/
1616
@PluginApi
1717
void load(BiConsumer<Plugin, PluginManifest> onLoad, PluginManifest pluginManifest);
18+
19+
@PluginApi
20+
default void load(BiConsumer<Plugin, PluginManifest> onLoad, PluginManifest pluginManifest, PluginLoaderContext context){
21+
load(onLoad, pluginManifest);
22+
}
1823
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package se.llbit.chunky.plugin.loader;
2+
3+
import se.llbit.chunky.plugin.PluginApi;
4+
5+
@PluginApi
6+
public class PluginLoaderContext {
7+
private boolean isHeadless;
8+
9+
public PluginLoaderContext(boolean isHeadless) {
10+
this.isHeadless = isHeadless;
11+
}
12+
13+
/**
14+
* Check if Chunky is running in headless mode. If it is, JavaFX might not be available.
15+
*
16+
* @return True if Chunky is running in headless mode, false otherwise
17+
*/
18+
@PluginApi
19+
public boolean isHeadless() {
20+
return isHeadless;
21+
}
22+
}

chunky/src/java/se/llbit/chunky/plugin/loader/PluginManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public PluginManager(PluginLoader pluginLoader) {
4343
this.pluginLoader = pluginLoader;
4444
}
4545

46-
public void load(Set<PluginManifest> pluginManifests, BiConsumer<Plugin, PluginManifest> onLoad) {
46+
public void load(Set<PluginManifest> pluginManifests, BiConsumer<Plugin, PluginManifest> onLoad, PluginLoaderContext context) {
4747
// create plugin objects
4848
Map<String, List<ResolvedPlugin>> pluginsByName = new HashMap<>();
4949
pluginManifests.forEach(manifest -> {
@@ -81,7 +81,7 @@ public void load(Set<PluginManifest> pluginManifests, BiConsumer<Plugin, PluginM
8181
plugin.getManifest().getDependencies().stream().map(PluginDependency::toString).collect(Collectors.joining(", ")),
8282
plugin.getDependencies().stream().map(ResolvedPlugin::toString).collect(Collectors.joining(", "))
8383
);
84-
pluginLoader.load(onLoad, plugin.getManifest());
84+
pluginLoader.load(onLoad, plugin.getManifest(), context);
8585
}
8686
});
8787
}

chunky/src/java/se/llbit/chunky/plugin/manifest/PluginManifest.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
55
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
66
import org.apache.maven.artifact.versioning.VersionRange;
7+
import se.llbit.chunky.main.Chunky;
78
import se.llbit.chunky.main.Version;
89
import se.llbit.json.JsonMember;
910
import se.llbit.json.JsonObject;
@@ -25,6 +26,7 @@ public class PluginManifest {
2526
public final ArtifactVersion version;
2627
public final VersionRange targetVersion;
2728
public final String main;
29+
public final String mainHeadless;
2830
private final Set<PluginDependency> dependencies;
2931

3032
/**
@@ -38,14 +40,15 @@ public class PluginManifest {
3840
* @param dependencies The required dependencies of the plugin
3941
*/
4042
public PluginManifest(File pluginJar, String name, String author, String description, ArtifactVersion version,
41-
VersionRange targetVersion, String main, Set<PluginDependency> dependencies) {
43+
VersionRange targetVersion, String main, String mainHeadless, Set<PluginDependency> dependencies) {
4244
this.pluginJar = pluginJar;
4345
this.name = name;
4446
this.author = author;
4547
this.description = description;
4648
this.version = version;
4749
this.targetVersion = targetVersion;
4850
this.main = main;
51+
this.mainHeadless = mainHeadless;
4952
this.dependencies = dependencies;
5053
}
5154

@@ -62,6 +65,7 @@ public static Optional<PluginManifest> parse(JsonObject manifest, File pluginJar
6265
String version = manifest.get("version").stringValue("");
6366
String targetVersion = manifest.get("targetVersion").stringValue("");
6467
String main = manifest.get("main").stringValue("");
68+
String mainHeadless = manifest.get("mainHeadless").stringValue("");
6569

6670
if (name.isEmpty()) {
6771
Log.errorf("Plugin %s has no name specified", pluginJar.getName());
@@ -75,6 +79,9 @@ public static Optional<PluginManifest> parse(JsonObject manifest, File pluginJar
7579
Log.errorf("Plugin %s has no main class specified", name);
7680
return Optional.empty();
7781
}
82+
if (mainHeadless.isEmpty()) {
83+
mainHeadless = main;
84+
}
7885
if (targetVersion.isEmpty()) {
7986
Log.errorf("Plugin %s has no targetVersion specified", name);
8087
return Optional.empty();
@@ -111,7 +118,7 @@ public static Optional<PluginManifest> parse(JsonObject manifest, File pluginJar
111118
Log.error(String.format("Failed to parse plugin %s targetVersion %s into version range.", name, targetVersion), e);
112119
targetVersionRange = VersionRange.createFromVersion(targetVersion);
113120
}
114-
return Optional.of(new PluginManifest(pluginJar, name, author, description, new DefaultArtifactVersion(version), targetVersionRange, main, dependencies));
121+
return Optional.of(new PluginManifest(pluginJar, name, author, description, new DefaultArtifactVersion(version), targetVersionRange, main, mainHeadless, dependencies));
115122
}
116123

117124
public Set<PluginDependency> getDependencies() {

chunky/src/test/se/llbit/chunky/plugin/PluginManagerTest.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.apache.maven.artifact.versioning.VersionRange;
66
import org.junit.jupiter.api.Test;
77
import se.llbit.chunky.main.Version;
8+
import se.llbit.chunky.plugin.loader.PluginLoaderContext;
89
import se.llbit.chunky.plugin.loader.PluginManager;
910
import se.llbit.chunky.plugin.manifest.PluginDependency;
1011
import se.llbit.chunky.plugin.manifest.PluginManifest;
@@ -26,14 +27,14 @@ public void testSimpleChainOfDependencies() throws InvalidVersionSpecificationEx
2627
HashSet<PluginManifest> manifests = new HashSet<>();
2728
for (int i = 1; i < 10; i++) {
2829
manifests.add(new PluginManifest(null, "test" + i, "author" + i, "desc" + i,
29-
new DefaultArtifactVersion(i + ".0"), chunkyVersion, "test1.Main",
30+
new DefaultArtifactVersion(i + ".0"), chunkyVersion, "test1.Main", "test1.MainHeadless",
3031
Set.of(
3132
new PluginDependency("test" + (i + 1), VersionRange.createFromVersionSpec("[" + (i + 1) + ".0]"))
3233
)
3334
));
3435
}
3536
manifests.add(new PluginManifest(null, "test10", "author10", "desc10",
36-
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main", Collections.emptySet()
37+
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main", "test10.MainHeadless", Collections.emptySet()
3738
));
3839

3940
Set<String> expectedLoadedPlugins = Set.of("test1", "test2", "test3", "test4", "test5", "test6", "test7", "test8", "test9", "test10");
@@ -52,15 +53,15 @@ public void testLoopOfDependencies() throws InvalidVersionSpecificationException
5253
HashSet<PluginManifest> manifests = new HashSet<>();
5354
for (int i = 1; i < 10; i++) {
5455
manifests.add(new PluginManifest(null, "test" + i, "author" + i, "desc" + i,
55-
new DefaultArtifactVersion(i + ".0"), chunkyVersion, "test1.Main",
56+
new DefaultArtifactVersion(i + ".0"), chunkyVersion, "test1.Main", "test1.MainHeadless",
5657
Set.of(
5758
new PluginDependency("test" + (i + 1), VersionRange.createFromVersionSpec("[" + (i + 1) + ".0]"))
5859
)
5960
));
6061
}
6162
// The last one depends on the first. A loop!
6263
manifests.add(new PluginManifest(null, "test10", "author10", "desc10",
63-
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main",
64+
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main", "test10.MainHeadless",
6465
Set.of(
6566
new PluginDependency("test1", VersionRange.createFromVersionSpec("[1.0]"))
6667
)
@@ -83,62 +84,62 @@ public void testNormalCase() throws InvalidVersionSpecificationException {
8384
*/
8485
Set<PluginManifest> manifests = new HashSet<>();
8586
manifests.add(new PluginManifest(null, "test10", "author10", "desc10",
86-
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main",
87+
new DefaultArtifactVersion("10.0"), chunkyVersion, "test10.Main", "test10.MainHeadless",
8788
Collections.emptySet()
8889
));
8990
manifests.add(new PluginManifest(null, "test9", "author9", "desc9",
90-
new DefaultArtifactVersion("9.0"), chunkyVersion, "test9.Main",
91+
new DefaultArtifactVersion("9.0"), chunkyVersion, "test9.Main", "test9.MainHeadless",
9192
Set.of(
9293
new PluginDependency("test8", VersionRange.createFromVersionSpec("[8.0]")),
9394
new PluginDependency("test7", VersionRange.createFromVersionSpec("[7.0]"))
9495
)
9596
));
9697
manifests.add(new PluginManifest(null, "test8", "author8", "desc8",
97-
new DefaultArtifactVersion("8.0"), chunkyVersion, "test8.Main",
98+
new DefaultArtifactVersion("8.0"), chunkyVersion, "test8.Main", "test8.MainHeadless",
9899
Set.of(
99100
new PluginDependency("test5", VersionRange.createFromVersionSpec("[5.0]"))
100101
)
101102
));
102103
manifests.add(new PluginManifest(null, "test7", "author7", "desc7",
103-
new DefaultArtifactVersion("7.0"), chunkyVersion, "test7.Main",
104+
new DefaultArtifactVersion("7.0"), chunkyVersion, "test7.Main", "test7.MainHeadless",
104105
Set.of(
105106
new PluginDependency("test4", VersionRange.createFromVersionSpec("[4.0]")),
106107
new PluginDependency("test2", VersionRange.createFromVersionSpec("[2.0]"))
107108
)
108109
));
109110
manifests.add(new PluginManifest(null, "test6", "author6", "desc6",
110-
new DefaultArtifactVersion("6.0"), chunkyVersion, "test6.Main",
111+
new DefaultArtifactVersion("6.0"), chunkyVersion, "test6.Main", "test6.MainHeadless",
111112
Set.of(
112113
new PluginDependency("test7", VersionRange.createFromVersionSpec("[7.0]")),
113114
new PluginDependency("test3", VersionRange.createFromVersionSpec("[3.0]"))
114115
)
115116
));
116117
manifests.add(new PluginManifest(null, "test5", "author5", "desc5",
117-
new DefaultArtifactVersion("5.0"), chunkyVersion, "test5.Main",
118+
new DefaultArtifactVersion("5.0"), chunkyVersion, "test5.Main", "test5.MainHeadless",
118119
Set.of(
119120
new PluginDependency("test4", VersionRange.createFromVersionSpec("[4.0]"))
120121
)
121122
));
122123
manifests.add(new PluginManifest(null, "test4", "author4", "desc4",
123-
new DefaultArtifactVersion("4.0"), chunkyVersion, "test4.Main",
124+
new DefaultArtifactVersion("4.0"), chunkyVersion, "test4.Main", "test4.MainHeadless",
124125
Set.of(
125126
new PluginDependency("test10", VersionRange.createFromVersionSpec("[10.0]"))
126127
)
127128
));
128129
manifests.add(new PluginManifest(null, "test3", "author3", "desc3",
129-
new DefaultArtifactVersion("3.0"), chunkyVersion, "test3.Main",
130+
new DefaultArtifactVersion("3.0"), chunkyVersion, "test3.Main", "test3.MainHeadless",
130131
Set.of(
131132
new PluginDependency("test2", VersionRange.createFromVersionSpec("[2.0]"))
132133
)
133134
));
134135
manifests.add(new PluginManifest(null, "test2", "author2", "desc2",
135-
new DefaultArtifactVersion("2.0"), chunkyVersion, "test2.Main",
136+
new DefaultArtifactVersion("2.0"), chunkyVersion, "test2.Main", "test2.MainHeadless",
136137
Set.of(
137138
new PluginDependency("test10", VersionRange.createFromVersionSpec("[10.0]"))
138139
)
139140
));
140141
manifests.add(new PluginManifest(null, "test1", "author1", "desc1",
141-
new DefaultArtifactVersion("1.0"), chunkyVersion, "test1.Main",
142+
new DefaultArtifactVersion("1.0"), chunkyVersion, "test1.Main", "test1.MainHeadless",
142143
Set.of(
143144
new PluginDependency("test9", VersionRange.createFromVersionSpec("[9.0]")),
144145
new PluginDependency("test6", VersionRange.createFromVersionSpec("[6.0]"))
@@ -164,7 +165,7 @@ private static void assertLoadOrder(Set<PluginManifest> manifests, Set<String> e
164165
loadedPlugins.add(pluginManifest.name);
165166
});
166167

167-
pluginLoader.load(manifests, (plugin, manifest) -> {});
168+
pluginLoader.load(manifests, (plugin, manifest) -> {}, new PluginLoaderContext(true));
168169
assertEquals(expectedPlugins, loadedPlugins);
169170
}
170171
}

0 commit comments

Comments
 (0)