Feature image for "No more Java in vscode-xml!"

Among other improvements and bug fixes in the vscode-xml extension 0.15.0 release, you can now run the extension without needing Java. We know the Java requirement discouraged many people from trying the extension. We have included a new setting, Prefer Binary (xml.server.preferBinary) that lets you choose between the Java server and the new binary server. We're excited to remove the Java restriction from Red Hat’s XML extension for Visual Studio Code in vscode-xml 0.15.0. Keep reading to find out how we did it.

LemMinX, Java, and vscode-xml

Eclipse LemMinX is the language server that provides vscode-xml's XML editing features. By creating a language server, we can provide XML editing capabilities not only to Visual Studio Code (VS Code) but also to other text editors, such as Sublime, Eclipse IDE, Emacs, and Vim.

Note: To learn more about language servers and the Language Server Protocol (LSP), please see A common interface for building developer tools, which does a great job of explaining the topic.

LemMinX was written in Java. Using Java to code LemMinX allows the use of existing XML libraries. For instance, LemMinX uses the Xerces library to validate XML files against schemas. Until the vscode-xml 0.15.0 release, we required a Java Runtime Environment (JRE), because we distributed LemMinX as a JAR file that had to be interpreted by the Java runtime. LemMinX also supports a number of useful extensions, either as individual JAR files or as a ZIP file containing multiple JARs. Current extensions include lemminx-maven (for Maven pom.xml files), liquibase-lsp (for liquibase XML files), and lemminx-liberty (for OpenLiberty server.xml files).

To allow the user to run vscode-xml without installing Java, we needed to rethink how we provided LemMinX functionality to the user.

GraalVM native-image

GraalVM native-image is a tool that can compile a Java program into a standalone, platform-specific executable binary. Unlike other tools, it doesn’t simply package a copy of Java along with the program. GraalVM compiles the Java bytecode into native instructions, and includes a small library that manages memory. When we use GraalVM, the generated binary doesn't include all the code that normally goes into a Java installation. The executable binary in LemMinX is even smaller than these minimal Java installations.

Figure 1 shows the sequence by which GraalVM native-image creates an executable. The Java compiler transforms Java source code into Java bytecode. Then, GraalVM native-image turns the Java bytecode into native instructions.

A diagram that shows the steps to transform Java source code into a native binary
Java source code is turned into Java byte code by the Java compiler. Then, GraalVM native-image turns the Java byte code into native instructions.
Figure 1: How GraalVM native-image transforms Java bytecode into native instructions.

GraalVM native-image has limitations. The binary that GraalVM produces works only for a specific operating system and CPU architecture. As a result, we need to compile and distribute separate binaries for Windows, macOS, and Linux. The macOS binary is compiled for the x86_64 CPU architecture but also works on Apple Silicon through the Rosetta 2 translation layer. Another limitation of GraalVM native-image is that all the code that you want to run must be present during the native-image process. Extensions to the base XML editing functionality, such as lemminx-maven, liquibase-lsp, and lemminx-liberty, won't work when using the binary server.

Delivering the binaries

We do not package the binaries into vscode-xml. Instead, when vscode-xml starts up and detects that you don’t have Java installed, it downloads the binary specific to your computer’s operating system. This workaround reduces the total amount of data downloaded to make vscode-xml work. The extension comes with the SHA 256 checksums of the binaries so that it can check the integrity of the downloaded binary.

When will vscode-xml use a binary?

You can use the flowchart in Figure 2 to figure out whether vscode-xml will download and run a binary. Note that you can enable the Prefer Binary setting (xml.server.preferBinary) if you have a Java runtime but still want VS Code to use the binary.

A flowchart of the steps taken to determine if a binary or Java server should be started
If Java is not installed, vscode-xml launches the binary server. If Java is installed and the xml.server.preferBinary option is set to false, then vscode-xml launches the Java server. If Java is installed, and the xml.server.preferBinary option is set to true, then if vscode-xml detects LemMinX extensions, the Java server is used, and otherwise a binary server is used.
Figure 2: How vscode-xml determines whether to start a binary or a Java server.

If Java is not installed, vscode-xml launches the binary server. If it is installed and the Prefer Binary setting is disabled, vscode-xml launches the Java server. If it is installed and the Prefer Binary setting is enabled, vscode-xml launches the Java server if LemMinX extensions are detected; otherwise, it launches the binary server.

The LemMinX server extensions shown in Figure 2 are extensions onto vscode-xml’s functionality, as I described earlier. If the extensions are present along with the Java runtime, they override the Prefer Binary setting because these extensions won’t work with the binary server.

Conclusion

This article offered an inside look at how we removed the Java requirement in vscode-xml. Thanks to everybody who contributed to this release! To see a list of all the changes and bug fixes, please see the vscode-xml 0.15.0 changelog. If you encounter bugs or have ideas for improving vscode-xml, we hope you will submit an issue.

Last updated: March 31, 2021