PL/Java and the Java Platform Module System

Java 9 introduced the Java Platform Module System (JPMS), allowing Java code to be strongly encapsulated into modules that declare their dependency relationships to other modules explicitly.

One consequence is that where a pre-Java-9 Java runtime would have a “class path”, a Java-9-or-later runtime has a “module path” as well as a legacy class path.

Pre-1.6.0 releases of PL/Java were not modularized, and can be used on older Java runtimes. They will also run on Java 9 and later, but still only as non-modular code. The Java runtime is launched with a class path that includes a PL/Java jar and usually nothing else. That single jar includes both API and implementation classes.

PL/Java 1.6.0 or later is structured as an API module (named org.postgresql.pljava) and an implementation module (named org.postgresql.pljava.internal) supplied in two distinct jar files. The Java runtime is launched with a module path, not a class path, that names both the implementation and the API jar, and usually nothing else. For a class path, the runtime is launched by default with none at all.

User code developed to run in PL/Java is normally installed via SQL calling the sqlj.install_jar and sqlj.set_classpath functions, and these mechanisms are independent of the Java runtime's class path at launch.

In Java 9 and later, both a module path and a class path are supported so that newer, modularized code and legacy, non-modular code can interoperate, and legacy code can be migrated over time:

As of PL/Java 1.6, while PL/Java's own implementation is modularized, it implements a version of the ISO SQL Java Routines and Types specification that does not include Java module system concepts. Its sqlj.set_classpath function manipulates an internal class path, not a module path, and a jar installed with sqlj.install_jar behaves as legacy code in an unnamed module.

Readable versus observable modules

Using Java's terminology, modules that can be found on the module path are observable. Not all of those are automatically readable; the readable ones in a JVM instance are initially those encountered, at JVM start-up, in the “recursive enumeration” step of module resolution.

Recursive enumeration begins with some root modules, and proceeds until all of the modules on which they (transitively) depend have been added to the readable set. When PL/Java is launched in a session, PL/Java's own module is a root, and so the readable modules will include those PL/Java itself depends on, such as java.base, java.sql, and the other modules java.sql names with requires transitive directives.

Those modules may be enough for many uses of PL/Java. However, if code for use in PL/Java will refer to other modules, --add-modules in pljava.vmoptions can be used to add more roots. Because of recursive enumeration, it is enough to add just one module, or a few modules, whose dependencies recursively cover whatever modules will be needed.

At one extreme for convenience, Java provides a module, java.se, that simply declares dependencies on the other modules that make up the full Java SE API. Therefore, --add-modules=java.se will ensure that any PL/Java code is able to refer to any of the Java SE API. However, PL/Java instances may use less memory and start up more quickly if an effort is made to add only modules actually needed.

Configuring the launch-time module path

The configuration variable pljava.module_path controls the module path used to launch the Java runtime. Its default is constructed to include the expected locations of PL/Java's own implementation and API jars, and nothing else, so there is typically no need to set it explicitly unless those jars have been installed at unusual locations. Its syntax is simply the pathnames of the jar files, separated by the correct path separator character for the platform (often a colon, or a semicolon on Windows).

There may at times be a reason to place additional modular jars on this module path. Whenever it is explicitly set, it must still include the correct locations of the PL/Java implementation and API jars.

Configuring the launch-time class path

The launch-time class path has an empty default, which means (because PL/Java has a main module) that there is no class path. It does not default to finding class files in the backend's current directory (which, in a pre-Java-9 runtime, is what an empty class path would mean).

There may at times be a reason to place some jar or jars on the launch-time class path, rather than installing them in SQL with sqlj.install_jar in the usual way. It can be set by adding a -Djava.class.path=... in the pljava.vmoptions configuration variable. Like the module path, its syntax is simply the jar file pathnames, separated by the platform's path separator character.