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.
- 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)
- the appending-to-the-system-class-loader approach doesn't work anyway starting with java9 - Java 9, compatability issue with ClassLoader.getSystemClassLoader
- 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.
- 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).
- 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:
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
Printing a JPAnel with Scrollable Jtable on It
The Easiest Way to Transform Collection to Array
Determine If a Java Application Is in Debug Mode in Eclipse
Convert an Xml File to CSV File Using Java
Comparing Date Strings in Java
How to Add Button in a Row of Jtable in Swing Java
Can't Rid of 'T' in Localdatetime
How to Tell Spring Boot Which Main Class to Use for the Executable Jar
Javax.Mail.Authenticationfailedexception Is Thrown While Sending Email in Java
Jcomponent Stops Getting Rendered Once It Goes Off the Screen
Creating New Generic Object with Wildcard
How to Unit Test Abstract Classes: Extend with Stubs