A long time ago I wrote a post on how to build a native-image with GraalVM.
Lately, I have been doing the same in the context of Quarkus.
In this post I want describe what I have learned about native-image and reflection in the context of Quarkus; but not necessarily limited to Quarkus.
It started with me wanting to build a native application for a simple Quarkus application that uses a JDK API for XML processing.
I.e. it uses code like this:
private boolean isValidXmlFile(Path p) {
try {
if (p == null) return false;
if (!p.toFile().exists()) return false;
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.parse(new InputSource(new FileInputStream(p.toFile())));
return true;
}
catch (SAXParseException spe) {
return false;
}
catch (Exception e) {
logger.error(String.format("Error while determining if file (%s) is a valid XML-file.", p.getFileName().toString()), e);
return false;
}
}
I tried to build a native image by executing ./gradlew nativeImage
and got this error when runing the native application.
Exception in thread "main" javax.xml.parsers.FactoryConfigurationError: Provider com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl not found
at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:194)
at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:147)
at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:271)
at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:147)
at de.dplatz.bpmndiff.entity.Diff.isValidXmlFile(Diff.java:122)
at de.dplatz.bpmndiff.entity.Diff.determineIfSupported(Diff.java:113)
at de.dplatz.bpmndiff.entity.Diff.ofPaths(Diff.java:95)
at de.dplatz.bpmndiff.entity.Diff.ofPaths(Diff.java:73)
at de.dplatz.bpmndiff.control.Differ.diff(Differ.java:39)
at de.dplatz.bpmndiff.boundary.DiffResource.diff(DiffResource.java:31)
at de.dplatz.bpmndiff.boundary.DiffResource_ClientProxy.diff(DiffResource_ClientProxy.zig:51)
at de.dplatz.bpmndiff.UICommand.call(UICommand.java:65)
at de.dplatz.bpmndiff.UICommand.call(UICommand.java:27)
at picocli.CommandLine.executeUserObject(CommandLine.java:1783)
at picocli.CommandLine.access$900(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2150)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2144)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2108)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:1975)
at picocli.CommandLine.execute(CommandLine.java:1904)
at de.dplatz.bpmndiff.UICommand.run(UICommand.java:55)
at de.dplatz.bpmndiff.UICommand_ClientProxy.run(UICommand_ClientProxy.zig:72)
at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:111)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:61)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:38)
at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:30)
Caused by: java.lang.ClassNotFoundException: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
at java.lang.Class.forName(DynamicHub.java:1197)
at javax.xml.parsers.FactoryFinder.getProviderClass(FactoryFinder.java:119)
at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:183)
... 25 more
If you have read my previous post, you already know that a JSON-file needs to be provided to native-image so reflection can be used on these classes during runtime of the native application.
Based on the error, I was able to construct a file reflect-config.json
with this content:
[
{
"name": "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
}
]
Where does this file have to be placed so native-image picks it up? For Quarkus, there are three options:
For some reason, this third and simplest solution is not mentioned in the Quarkus guide; but maybe this is a new feature in GraalVM.