简体   繁体   中英

Add or remove specific jars from custom ClassLoader

I want to create dynamically a classloader for executing JSR223 script in a controlled environment but failing,

I'm trying remove/add jars using current(parent) ClassLoader, I tried solution Dynamically removing jars from classpath

 public class DistributionClassLoader extends ClassLoader { public DistributionClassLoader(ClassLoader parent) { super(parent); } private Map<String, ClassLoader> classLoadersByDistribution = Collections.synchronizedMap(new WeakHashMap<>()); private final AtomicReference<String> distribution = new AtomicReference<>(); @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { final ClassLoader delegate = classLoadersByDistribution.get(distribution.get()); if (delegate != null) return Class.forName(name, true, delegate); throw new ClassNotFoundException(name); } public void addDistribution(String key, ClassLoader distributionClassLoader){ classLoadersByDistribution.put(key,distributionClassLoader); } public void makeDistributionActive(String key){distribution.set(key);} public void removeDistribution(String key){ final ClassLoader toRemove = classLoadersByDistribution.remove(key); } } 

But it didn't include all my jars, in test this work

ClassLoader cl = this.getClass().getClassLoader();
Class cls = cl.loadClass("org.springframework.http.HttpStatus");

But using the solution doesn't find class

ClassLoader cl = new DistributionClassLoader(this.getClass().getClassLoader());
Class cls = cl.loadClass("org.springframework.http.HttpStatus");

Exception:

java.lang.ClassNotFoundException: org.springframework.http.HttpStatus
    at com.DistributionClassLoader.loadClass(DistributionClassLoader.java:24)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

How can I select specific jars to add or remove from ClassLoader?

EDIT

I'm able to load jars using @czdepski answer but I still want to remove all/most classes except JDK's

 Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class}); sysMethod.setAccessible(true); sysMethod.invoke(sysLoader, new Object[]{url}); 

You got the delegation wrong. You never check the parent class loader if it has this class.

If we look at the Javadoc for ClassLoader.loadClass(String,boolean) we find:

Loads the class with the specified binary name. The default implementation of this method searches for classes in the following order:

  1. Invoke findLoadedClass(String) to check if the class has already been loaded.

  2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built into the virtual machine is used, instead.

  3. Invoke the findClass(String) method to find the class.

If the class was found using the above steps, and the resolve flag is true, this method will then invoke the resolveClass(Class) method on the resulting Class object.

Subclasses of ClassLoader are encouraged to override findClass(String), rather than this method.

You did override loadClass , but don't do any delegation to it's parent ClassLoader.
Instead you call classLoadersByDistribution.get(distribution.get()); , which is most likely null (hard to tell, but always expect WeakHashMap.get() to return null ).

If delegate is not null, then you try to load the class from there. This means the loaded class won't use your ClassLoader to load new classes, but instead the ClassLoader you delegated to.

After all, this sounds like a XY Problem . You want to execute some code using the scripting API and somehow control the environment.
Did you try to use a SecurityManager ?


About your comment that you need your own ClassLoader to create a ScriptEngineManager : This ClassLoader is used to search for ScriptEngineFactory implementations. This is done using a service provider interface .
If you don't use your own script engine, this should not matter to you.


If your goal is to add a few jars so the engine can use it, create a new URLClassLoader with the platform class loader as parent. (Or extension class loader, depends on the java version.)
Set this ClassLoader as Thread.setContextClassLoader() and create the ScriptEngine.

If you did choose the parent of the URLClassLoader correctly, it will not see classes loadable by the application class loader.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM