Java 9, Compatability Issue with Classloader.Getsystemclassloader

Migration to java11 ClassCastException

Java is backwards-compatible. Looking at ClassLoader.getSystemClassLoader() in Java 8 shows that it returs a ClassLoader-instance, not a URLClassLoader-instance. The same is true for ClassLoader.getSystemClassLoader() in Java 11. Thus, there were never any guarantees that ClassLoader.getSystemClassLoader() returns an URLClassLoader.


As far as I know, there is no way to transform a ClassLoader to an URLClassLoader. The only proper way to fix this issue is to write the maintainers of the library and telling them that they relied on some implementation detail, that they shouldn't have done so and ask them to fix the issue. If it is an easy fix and the project is open-source, you can fork the project, fix the issue and create a pull request.

How do I get jjs --add-opens to work in java9?

As I commented above, there are actually 3 problems.

  1. the question as asked - Answer: a. doesn't help (see next point) b. the system level add-opens command line option doesn't make its way to the Nashorn engine used by jjs)
  2. the appending-to-the-system-class-loader approach doesn't work anyway starting with java9 - Java 9, compatability issue with ClassLoader.getSystemClassLoader
  3. starting with java-11, jjs itself is declared deprecated

However, thanks to hints from @Alan , @Holger and my colleague @Philippe , I got what I want with workarounds.

  1. You can create your own URLClassLoader using the desired jars and create a second nashorn engine passing in this classloader (and e.g. command line arguments from jjs).
  2. Add another hack for implementing a so-called "here document" for the script-within-a-script

... and here is a complete example:

// jjs -scripting ora2csv.js -- "select 'hi' from dual" jdbc:oracle:thin:@host:1521:XE user pass

function newJjsEngineWith (jars) {
var ua = Java.type("java.net.URL[]"); var urls = new ua(jars.length);
for(var i=0; i<jars.length; i++) {
var u=new java.net.URL(new java.io.File(jars[i]).toURL());
urls[i] = u;
}
var loader = new java.net.URLClassLoader(urls);
java.lang.Thread.currentThread().setContextClassLoader(loader);
var nsef = Java.type("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
var sa = Java.type("java.lang.String[]"); var args = new sa(2 + $ARG.length);
args[0] = "-scripting"; args[1] = "--";
for(var i=0;i<$ARG.length;i++) { args[i+2] = $ARG[i]; }
return new nsef().getScriptEngine(args, loader);
}

var jjs = newJjsEngineWith(["ojdbc8.jar"]);

function hereDoc(f) { return f.toString().slice(14,-3); }

var code = hereDoc(function(){/*
var q = $ARG[0]; // select 'hello' from dual
var c = $ARG[1]; // jdbc:oracle:thin:@host:1521:XW
var u = $ARG[2]; // username
var p = $ARG[3]; // password
var conn = java.sql.DriverManager.getConnection(c, u, p);
var stmt = conn.createStatement(); rset = stmt.executeQuery(q);
var row=0;
while (rset.next()) {
row++; var rowBuf = new java.lang.StringBuilder();
var meta = rset.getMetaData();
for(var col=0; col< meta.getColumnCount(); col++) {
var cell = rset.getString(col+1);
if (cell == null) { cell = ""; }
rowBuf.append(cell.replaceAll(";",","));
if (col < meta.getColumnCount()-1) { rowBuf.append(";"); }
}
print(rowBuf.toString());
}
stmt.close();
*/});

jjs.eval(code);

This works for java 8 through java 11.

$ /usr/lib/jvm/java-1.8.0/bin/jjs -scripting ora2csv.js -- "select 'hi' from dual" jdbc:oracle:thin:@host:1521:XE user pass
hi
$ /usr/lib/jvm/java-9/bin/jjs -scripting ora2csv.js -- "select 'hi' from dual" jdbc:oracle:thin:@host:1521:XE user pass
hi
$ /usr/lib/jvm/java-10/bin/jjs -scripting ora2csv.js -- "select 'hi' from dual" jdbc:oracle:thin:@host:1521:XE user pass
hi
$ /usr/lib/jvm/java-11/bin/jjs -scripting ora2csv.js -- "select 'hi' from dual" jdbc:oracle:thin:@host:1521:XE user pass
Warning: The jjs tool is planned to be removed from a future JDK release
Warning: Nashorn engine is planned to be removed from a future JDK release
hi

I welcome any suggestions for making this more concise and less pukey

Error with versions of JDK while creating report using PhpJasper

I have a solution of this problem.

I remove all java packages (sudo apt-get remove openjdk...) and install again with version Java JDK 1.8 (instruction for ubuntu)

After this I got correct version of Java:

java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

And I don't have errors in my project

How do I dynamically load modules from a directory in Java 9+

To load modules dynamically, you need to define a new ModuleLayer. The new module layer will inherit the boot layer:

Sample Image

This means that in your boot layer (where your main module is), you cannot directly refer to classes in the plugins layer. However, you can use your plugins layer through services.

Here is the code that you can use as a starting point:

Path pluginsDir = Paths.get("plugins"); // Directory with plugins JARs

// Search for plugins in the plugins directory
ModuleFinder pluginsFinder = ModuleFinder.of(pluginsDir);

// Find all names of all found plugin modules
List<String> plugins = pluginsFinder
.findAll()
.stream()
.map(ModuleReference::descriptor)
.map(ModuleDescriptor::name)
.collect(Collectors.toList());

// Create configuration that will resolve plugin modules
// (verify that the graph of modules is correct)
Configuration pluginsConfiguration = ModuleLayer
.boot()
.configuration()
.resolve(pluginsFinder, ModuleFinder.of(), plugins);

// Create a module layer for plugins
ModuleLayer layer = ModuleLayer
.boot()
.defineModulesWithOneLoader(pluginsConfiguration, ClassLoader.getSystemClassLoader());

// Now you can use the new module layer to find service implementations in it
List<Your Service Interface> services = ServiceLoader
.load(layer, <Your Service Interface>.class)
.stream()
.map(Provider::get)
.collect(Collectors.toList());

// Do something with `services`
...

Module layers are considered an advanced topic but I don't find it really difficult. The only key point you need to understand is that module layers are inherited. This means that from a child layer, you can only refer to classes of the parent layer but not vice versa. To do the opposite, you have to use the inversion of control which is implemented in the Java module system by ServiceLoader.



Related Topics



Leave a reply



Submit