The problem you are experiencing is known as classpath hell.
There is no such functionality in Java. The reason behind this is the design of classloaders, which can only load classes and not unload them. In all likelyhood, this is not going to happen even in the future.
You could use a special classloader to load different classes from different jars, but even this way you quickly stumble into other limitations. For example, a classloader should only load the class if the parent has not yet loaded it. This means that other parts of your code cannot use apache-commons.jar. You could separate your program into two parts and have each run in its own classloader/classpath, but then you'll realize you can't share objects between them anymore. And so on.
In the end, you can either (a) try to find a combination of jars that works with both, (b) fix one part of the program so that it works with the other jar, or (c) break the program into two parts and have each run in separate JVMs.
Answer from jurez on Stack OverflowThe problem you are experiencing is known as classpath hell.
There is no such functionality in Java. The reason behind this is the design of classloaders, which can only load classes and not unload them. In all likelyhood, this is not going to happen even in the future.
You could use a special classloader to load different classes from different jars, but even this way you quickly stumble into other limitations. For example, a classloader should only load the class if the parent has not yet loaded it. This means that other parts of your code cannot use apache-commons.jar. You could separate your program into two parts and have each run in its own classloader/classpath, but then you'll realize you can't share objects between them anymore. And so on.
In the end, you can either (a) try to find a combination of jars that works with both, (b) fix one part of the program so that it works with the other jar, or (c) break the program into two parts and have each run in separate JVMs.
You could use the maven-enforcer-plugin in your pom to force specific versions of the transitive dependencies. Conflicting library version in a java maven project
Please find below a snippet as technical example to demonstrate adding / removing a path.
create following source files in any directory
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Stack;
import sun.misc.URLClassPath;
public class EvilPathDemo {
public static void addPath(String path) throws Exception {
URL u = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL",
new Class[]{URL.class}
);
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
public static void removePath(String path) throws Exception {
URL url = new File(path).toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader)
ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Class<?> ucpClass = URLClassPath.class;
Field urlsField = ucpClass.getDeclaredField("urls");
urlsField.setAccessible(true);
Stack urls = (Stack) urlsField.get(ucp);
urls.remove(url);
}
public static void main(String[] args) throws Exception {
String parm = args.length == 1 ? args[0] : "";
String evilPath = "/tmp";
String classpath = System.getProperty("java.class.path");
boolean isEvilPathSet = false;
for (String path : classpath.split(File.pathSeparator)) {
if (path.equalsIgnoreCase(evilPath)) {
System.out.printf("evil path '%s' in classpath%n", evilPath);
isEvilPathSet = true;
break;
}
}
if (isEvilPathSet && parm.equalsIgnoreCase("REMOVE")) {
System.out.printf("evil path '%s' will be removed%n", evilPath);
removePath(evilPath);
}
tryToLoad("Foo");
if (parm.equalsIgnoreCase("ADD")) {
System.out.printf("evil path '%s' will be added%n", evilPath);
addPath(evilPath);
}
tryToLoad("Bar");
}
private static void tryToLoad(String className) {
try {
Class<?> foo = Class.forName(className);
System.out.printf("class loaded: %s%n", foo.getName());
} catch (ClassNotFoundException ex) {
System.out.println(ex);
}
}
}
.
public class Foo {
static {
System.out.println("I'm foo...");
}
}
.
public class Bar {
static {
System.out.println("I'm bar...");
}
}
compile them as follow
javac EvilPathDemo.java
javac -d /tmp Foo.java Bar.java
During the test we will try to load the classes Foo and Bar.
without /tmp in the classpath
java -cp . EvilPathDemo
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
adding /tmp to the classpath
java -cp . EvilPathDemo add
java.lang.ClassNotFoundException: Foo
evil path '/tmp' will be added
I'm bar...
class loaded: Bar
with /tmp in the classpath
java -cp .:/tmp EvilPathDemo
evil path '/tmp' in the classpath
I'm foo...
class loaded: Foo
I'm bar...
class loaded: Bar
remove /tmp from the classpath
java -cp .:/tmp EvilPathDemo remove
evil path '/tmp' in the classpath
evil path '/tmp' will be removed
java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: Bar
During the testing I found out that following cases are not working.
- addPath(evilPath);
tryToLoad("Foo");
removePath(evilPath); // had not effect
tryToLoad("Bar"); - removePath(evilPath);
tryToLoad("Foo");
addPath(evilPath); // had no effect
tryToLoad("Bar"); - tryToLoad("Foo");
removePath(evilPath); // had no effect
tryToLoad("Bar");
I did not spent time to find out why. Because I don't see any practical use in it. If you really need/wish to play with the classpaths have a look how classloaders are working.
The removePath method from above did not work for me and my Weld Container, the url stack was always emtpy.
The following ugly smugly method worked:
public static void removeLastClasspathEntry() throws Exception {
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<?> urlClass = URLClassLoader.class;
Field ucpField = urlClass.getDeclaredField("ucp");
ucpField.setAccessible(true);
URLClassPath ucp = (URLClassPath) ucpField.get(urlClassLoader);
Field loadersField = URLClassPath.class.getDeclaredField("loaders");
loadersField.setAccessible(true);
List jarEntries = (List) loadersField.get(ucp);
jarEntries.remove(jarEntries.size() - 1);
Field pathField = URLClassPath.class.getDeclaredField("path");
pathField.setAccessible(true);
List pathList = (List) pathField.get(ucp);
URL jarUrl = (URL) pathList.get(pathList.size() - 1);
String jarName = jarUrl.toString();
pathList.remove(pathList.size() - 1);
Field lmapField = URLClassPath.class.getDeclaredField("lmap");
lmapField.setAccessible(true);
Map lmapMap = (Map) lmapField.get(ucp);
lmapMap.remove(jarName.replaceFirst("file:/", "file:///"));
}
java - Eclipse- How to remove jars which are added "from class path" of referenced library - Stack Overflow
dynamic - How to exclude a specific jar from java classpath - Stack Overflow
java - Dynamically removing jars from classpath - Stack Overflow
How to remove .jar files/locations from CLASSPATH?
To remove a library reference from the project classpath, follow this procedure:
1.Right-click on the project in the Project Explorer view and select Properties from the drop-down menu.This will open the Propertis dialog.
2.On the Propertis dialog, select the Java Build Path from the list of properties.
3.On the Java Build Path part of the dialog, select the Libraries tab.
4.Find the entry in the list of libraries called Shared Library [], and then select it.
5.Click Remove.
Right click on project, goto Build Path -> Configure Build Path, then goto the Libraries Tab, select the Referenced library and click remove.
Title. I have set classpath in .profile and .bashrc. Apparently java -cp command doesn't have an option to remove.
There are multiple ways to do this:
Try to use winrar. You can open your jar in it, explore the directory containing the class file. You can delete and add class files.
If you don't want to use winrar then do like this:
Extract the jar using this command
jar -xvf yourjar.jar
It will explode the jar. Delete the old class file and add your updated class file
Recreate the jar using the following command
jar -cvf yourjar.jar directoryofexploderjar/
Extend class and rewrite method removing bug
Use JDEC to decompile and replace class ( http://jdec.sourceforge.net/ )