eo-yaml has a compile-time dependency on javax.json-api. That is, code that uses eo-yaml must have a compile-time dependency on javax.json or this happens:
untitled.java:
import com.amihaiemil.eoyaml.*;
void main() {
var ym = Yaml.createMutableYamlMappingBuilder()
.add("hello", "world")
.add("first", "last")
.build();
IO.println(ym);
}
$ javac -cp Downloads/eo-yaml-8.0.6.jar untitled.java
untitled.java:5: error: cannot access JsonStructure
.add("hello", "world")
^
class file for javax.json.JsonStructure not found
1 error
OK, so you can add it to the classpath and it works:
$ javac -cp Downloads/eo-yaml-8.0.6.jar:Downloads/javax.json-api-1.1.4.jar untitled.java
(no output)
And subsequently run it without javax.json-api and it's fine too.
$ java -cp .:Downloads/eo-yaml-8.0.6.jar untitled
hello: world
first: last
OK, but what about if your app is a modular app? ie: using the Java 9+ jigsaw module system? Here abbreviated to the same effect to reproduce on commandline with a compact source, but obviously the context in which I hit this was a full application project...
$ javac --module-path Downloads/eo-yaml-8.0.6.jar:Downloads/javax.json-api-1.1.4.jar \
--add-modules com.amihaiemil.eoyaml \
untitled.java
untitled.java:5: error: cannot access JsonStructure
.add("hello", "world")
^
class file for javax.json.JsonStructure not found
1 error
You can add javax.json-api to the module path, but it still fails! This is because the eo-yaml model does not require java.json.
Now, you can force it to read it with more commandlinery:
$ javac --module-path Downloads/eo-yaml-8.0.6.jar:Downloads/javax.json-api-1.1.4.jar \
--add-modules com.amihaiemil.eoyaml,java.json \
--add-reads com.amihaiemil.eoyaml=java.json \
untitled.java
(no output)
and again, it runs fine without the java.json dependency:
$ java --module-path Downloads/eo-yaml-8.0.6.jar --add-modules com.amihaiemil.eoyaml untitled
hello: world
first: last
But this isn't really ideal, especially in full application-dev settings, you shouldn't need to be messing about with --add-reads arguments to javaCompile if you can help it.
I think the correct solution is that the module-info.java file for the eo-yaml library needs to have an additional line:
requires static java.json;
And then you still need to have javax.json-api as a compileOnly dependency, but this means you can now compile code that uses eg: YamlMappingBuilder.
NB: I think you'd have the same problem if you were actually using javax.json-api in your modular application and trying to use the eo-yaml methods that use them; you'd get the same errors at runtime even if you managed to get it compiled, because the eo-yaml module doesn't require java.json. You'd need to add the --add-reads to runtime args as well, or possibly more.
(I'm not using javax.json-api, I'm using jakarta.json-api and an impl thereof, which is another matter. If this library were to switch to that, this issue would still need to be resolved.)
As it happens, for my purposes I found a workaround that needs no javax.json-api dependency at any point, nor the --add-reads argument, but it does it by using the java.lang.invoke functionality:
untitled.java:
import module com.amihaiemil.eoyaml;
void main() {
var ym = new MappingBuilder(Yaml.createMutableYamlMappingBuilder())
.add("hello", "world")
.add("first", "last")
.build();
IO.println(ym);
}
private static class MappingBuilder {
private static final Supplier<MethodHandle> ymbAdd = StableValue.supplier(() -> {
try {
return MethodHandles.lookup()
.findVirtual(YamlMappingBuilder.class, "add", MethodType.methodType(YamlMappingBuilder.class, String.class, String.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
}
});
private YamlMappingBuilder ymb;
private final MethodHandle addHandle;
private MappingBuilder(YamlMappingBuilder ymb) {
this.ymb = ymb;
this.addHandle = ymbAdd.get();
}
private MappingBuilder add(String key, String value) {
try {
this.ymb = (YamlMappingBuilder) addHandle.invoke(this.ymb, key, value);
return this;
} catch (Throwable e) {
throw e instanceof RuntimeException re ? re : new RuntimeException(e);
}
}
private YamlMapping build() {
return ymb.build();
}
}
$ javac --enable-preview --release 25 --module-path Downloads/eo-yaml-8.0.6.jar \
--add-modules com.amihaiemil.eoyaml \
untitled.java
Note: untitled.java uses preview features of Java SE 25.
Note: Recompile with -Xlint:preview for details.
$ java --enable-preview --module-path Downloads/eo-yaml-8.0.6.jar --add-modules com.amihaiemil.eoyaml untitled
hello: world
first: last
By the way if you use reflection rather than invoke, ie: to get the method using something like:
Method addMethod = YamlMappingBuilder.class.getMethod("add", String.class, String.class);
MethodHandle addHandle = MethodHandles.lookup().unreflect(addMethod);
... you'll just move that class file for javax.json.JsonStructure not found error to runtime instead of compiletime, as Class::getMethod actually gets all the methods, and will trip over the ones that depend on JsonStructure et al. Guess how I know this. 😀 Obviously this would apply to any use of reflection over these classes at runtime, for any purpose.
eo-yaml has a compile-time dependency on javax.json-api. That is, code that uses eo-yaml must have a compile-time dependency on javax.json or this happens:
untitled.java:
OK, so you can add it to the classpath and it works:
(no output)
And subsequently run it without javax.json-api and it's fine too.
OK, but what about if your app is a modular app? ie: using the Java 9+ jigsaw module system? Here abbreviated to the same effect to reproduce on commandline with a compact source, but obviously the context in which I hit this was a full application project...
You can add javax.json-api to the module path, but it still fails! This is because the eo-yaml model does not require java.json.
Now, you can force it to read it with more commandlinery:
(no output)
and again, it runs fine without the java.json dependency:
But this isn't really ideal, especially in full application-dev settings, you shouldn't need to be messing about with
--add-readsarguments to javaCompile if you can help it.I think the correct solution is that the
module-info.javafile for the eo-yaml library needs to have an additional line:And then you still need to have javax.json-api as a compileOnly dependency, but this means you can now compile code that uses eg: YamlMappingBuilder.
NB: I think you'd have the same problem if you were actually using javax.json-api in your modular application and trying to use the eo-yaml methods that use them; you'd get the same errors at runtime even if you managed to get it compiled, because the eo-yaml module doesn't require java.json. You'd need to add the
--add-readsto runtime args as well, or possibly more.(I'm not using javax.json-api, I'm using jakarta.json-api and an impl thereof, which is another matter. If this library were to switch to that, this issue would still need to be resolved.)
As it happens, for my purposes I found a workaround that needs no javax.json-api dependency at any point, nor the
--add-readsargument, but it does it by using thejava.lang.invokefunctionality:untitled.java:
By the way if you use reflection rather than invoke, ie: to get the method using something like:
... you'll just move that
class file for javax.json.JsonStructure not founderror to runtime instead of compiletime, asClass::getMethodactually gets all the methods, and will trip over the ones that depend onJsonStructureet al. Guess how I know this. 😀 Obviously this would apply to any use of reflection over these classes at runtime, for any purpose.