I've this code below which is used to load methods defined in some external jar
files and in the web application using Spring Boot 2.7.10 version, it can invoke these external methods at run time.
This works fine with java 11, but when upgraded to java 17, it has error:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected void java.net.URLClassLoader.addURL(java.net.URL) accessible: module java.base does not "opens java.net" to unnamed module @221af3c0
private static void addToClassPath(String jarFilePath) {
try {
File file = new File(jarFilePath);
if (!(file.exists() && file.canRead())) {
throw new UdfDirectoryNotAccessibleException(jarFilePath);
}
URL url = file.toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader) UdfInvoker.class.getClassLoader();
Class urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
// error here since java 17
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[] { url });
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new UdfFailedLoadingException(jarFilePath, ex.getMessage(), ex);
} catch (MalformedURLException ex) {
throw new UdfJarFileNotAccessibleException(jarFilePath, ex.getMessage(), ex);
}
}
Does anyone know how this code can work with java 17 or if it can change to use something else which can work with both java 11 and java 17? Thanks.
NOTE:
Thanks to VGR user, I managed to make it work with these steps below using ServiceLoader
from JDK 1.6:
First, in the project code I create a file call Service
package test
public interface Service {
public Result handle(List<Result> paramList);
}
Then, I create a separated java file out of the project code which will be built as external java file
package implement
import test.Service;
public class ServiceImp implements Service {
public Result handle(List<Result> paramList) {
// write code here
}
}
Then, I build the jar file for implement.jar
which contains SeviceImp.class
.
NOTE: in this jar file, it must contain this file path
META-INF/services/test.Service
and the file contains test.ServiceImp
.
In the project code, I have this method to load the given implement.jar
file.
URL[] urls = new URL[] { file.getAbsoluteFile().toURI().toURL()};
ClassLoader parent = UdfInvoker.class.getClassLoader();
URLClassLoader c = new URLClassLoader(urls, parent);
ServiceLoader<Service> sl = ServiceLoader.load(Service.class, c);
Iterator<Service> apit = sl.iterator();
while (apit.hasNext()) {
// Here you can store it to a list to reuse later or call it directly
apit.next().handle(new ArrayList<>());