Adding new paths for native libraries at runtime in Java
If you want to add some native libraries at runtime, it was tricky with Java 8+:
This was a valid solution:
fldUsrPaths.setAccessible(true);
//get array of paths
final String[] saPath = (String[])fldUsrPaths.get(null);
//check if the path to add is already present
for (String path : saPath)
{
if (path.equals(pPath))
{
return;
}
}
//add the new path
final String[] saNewPaths = Arrays.copyOf(saPath, saPath.length + 1);
saNewPaths[saNewPaths.length - 1] = pPath;
fldUsrPaths.set(null, saNewPaths);
Since Java 10+ it's not possible anymore, because of: JDK-8210522
Some details from Stackoverflow
If someone still needs a solution for Java 10+, here it is:
VarHandle usr_paths = cl.findStaticVarHandle(ClassLoader.class, "usr_paths", String[].class);
String[] path = (String[])usr_paths.get();
...
usr_paths.set(new String[] {"A", "B"});
This code won't work if you are using Java 8. If you want support for e.g. Java < 10, simply use reflection
Sounds simple, but it is not simple.....
Here's a working solution: toPDF project (search for addLibraryPath(String pPath)).
The code in detail:
Method mStaticLookup = clsMHandles.getMethod("lookup");
Object oStaticLookup = mStaticLookup.invoke(null);
Method mLookup = clsMHandles.getMethod("privateLookupIn", Class.class, Class.forName("java.lang.invoke.MethodHandles$Lookup"));
Object oLookup = mLookup.invoke(null, ClassLoader.class, oStaticLookup);
Method mFindStatic = oLookup.getClass().getMethod("findStaticVarHandle", Class.class, String.class, Class.class);
Object oVarHandle = mFindStatic.invoke(oLookup, ClassLoader.class, "usr_paths", String[].class);
//MethodHandle mh = MethodHandles.lookup().findVirtual(VarHandle.class, "get", MethodType.methodType(Object.class));
//mh.invoke(oVarHandle);
Method mFindVirtual = oStaticLookup.getClass().getMethod("findVirtual", Class.class, String.class, Class.forName("java.lang.invoke.MethodType"));
Class<?> clsMethodType = Class.forName("java.lang.invoke.MethodType");
Method mMethodType = clsMethodType.getMethod("methodType", Class.class);
Object oMethodHandleGet = mFindVirtual.invoke(oStaticLookup, Class.forName("java.lang.invoke.VarHandle"), "get", mMethodType.invoke(null, Object.class));
Method mMethodHandleGet = oMethodHandleGet.getClass().getMethod("invokeWithArguments", Object[].class);
String[] saPath = (String[])mMethodHandleGet.invoke(oMethodHandleGet, new Object[] {new Object[] {oVarHandle}});
//check if the path to add is already present
for (String path : saPath)
{
if (path.equals(pPath))
{
return;
}
}
//add the new path
final String[] saNewPaths = Arrays.copyOf(saPath, saPath.length + 1);
saNewPaths[saNewPaths.length - 1] = pPath;
//MethodHandle mh = MethodHandles.lookup().findVirtual(VarHandle.class, "set", MethodType.methodType(Void.class, Object[].class));
//mh.invoke(oVarHandle, new String[] {"GEHT"});
mMethodType = clsMethodType.getMethod("methodType", Class.class, Class.class);
Object oMethodHandleSet = mFindVirtual.invoke(oStaticLookup, Class.forName("java.lang.invoke.VarHandle"), "set", mMethodType.invoke(null, Void.class, Object[].class));
Method mMethodHandleSet = oMethodHandleSet.getClass().getMethod("invokeWithArguments", Object[].class);
mMethodHandleSet.invoke(oMethodHandleSet, new Object[] {new Object[] {oVarHandle, saNewPaths}});
Not simple!!!
Summarized: It's not easy but it's still possible. So.... why are things getting more and more complex?