I have created a class "Class B"
package com.b
public class B
{
public void printMsg()
{
System.out.println("Called");
}
}
I have created a jar file with below class "Class A"
package com.a
import com.b
public class A extends B
{
}
When i tried to load jar dynamically using below code, I am getting error "Class B" "classnotfoundexception"
ClassLoader cl = new URLClassLoader(new URL[] { new URL(jarFullPath) });
Class<?> cla = cl.loadClass(className);
Object obj = cla.newInstance();
I believe that it is because you did not provide any parent ClassLoader to your URLClassLoader such that it can find A but not B, try this instead:
ClassLoader cl = new URLClassLoader(
new URL[] {new URL(jarFullPath)}, Thread.currentThread().getContextClassLoader()
);
This use Thread.currentThread().getContextClassLoader() as parent ClassLoader corresponding to the context ClassLoader.
Related
I am trying to compile, load and use an inherited class at runtime. Here is the structure of my Java/Maven project. Note there are no .java files under com.mycompany.model.inherited. This is where generated class files will be placed.
src/main/java
com.mycompany.model.base
BaseClass.java
src/test/java
com.mycompany.model.inherited
BaseClass.java
package com.mycompany.model.base;
public abstract class BaseClass {
public abstract void doWork();
}
At runtime I write the contents of an inherited class to src/test/java/com/mycompany/inherited/SubClass.java
package com.mycompany.model.inherited;
import com.mycompany.model.BaseClass;
public class SubClass extends BaseClass {
#Override
public void doWork() {
System.out.println("SubClass is doing work.");
}
}
And then I execute this code to compile SubClass.java, load it, create an object instance. All of that works fine. But when I try to cast it to a BaseClass I'm getting a ClassCastException at runtime. Here's the full code.
// prepare the class file contents and write it to disk.
String subClassContents =
"package com.mycompany.model.inherited;\n" +
"\n" +
"import com.mycompany.model.base.BaseClass;\n" +
"\n" +
"public class SubClass extends BaseClass {\n" +
"\n" +
" #Override\n" +
" public void doWork() {\n" +
" System.out.println(\"SubClass is doing work.\");\n" +
" }\n" +
"}\n";
File file = new File(System.getProperty("user.dir") + "\\src\\test\\java\\com\\mycompany\\model\\inherited\\SubClass.java");
FileWriter writer = new FileWriter(file);
writer.write(subClassContents);
writer.close();
// compile the class using the current classpath.
File[] files = new File[]{file};
List<String> options = new ArrayList<>();
options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager filemanager = compiler.getStandardFileManager(null, null, null);
Iterable fileObjects = filemanager.getJavaFileObjects(files);
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, options, null, fileObjects);
task.call();
// load the class using the current classpath.
String[] paths = System.getProperty("java.class.path").split(";");
List<URL> urls = new ArrayList<>();
urls.add(new File("src/test/java").toURI().toURL());
for (String p : paths) {
urls.add(new File(p).toURI().toURL());
}
URLClassLoader classLoader = URLClassLoader.newInstance(urls.stream().toArray(URL[]::new));
Class<?> cls = Class.forName("com.mycompany.model.inherited.SubClass", true, classLoader);
// use the class.
BaseClass base = (BaseClass) cls.newInstance();
base.doWork();
java.lang.ClassCastException: com.mycompany.model.inherited.SubClass cannot be cast to com.mycompany.model.base.BaseClass
If I include src/test/java/com/mycompany/model/inherited/SubClass.java at compile time it works fine. I.e:
BaseClass base = (BaseClass) SubClass.class.getClassLoader().loadClass("com.mycompany.model.inherited.SubClass").newInstance();
base.doWork();
Hoping someone can point to the issue and help me solve it. Thanks!
The cast failed because the base class of SubClass is actually considered a different class from the "BaseClass" that you wrote in your source code. They are considered different because they are loaded by different class loaders. The base class of SubClass is loaded by the URLClassLoader you created, whereas the BaseClass you wrote in the source code is presumably loaded by the system class loader.
To fix this, you can set the parent class loader of the URLClassLoader to the same class loader as the one that loaded BaseClass, and only give src/test/java as the class path. This way, when the URLClassLoader loads SubClass, it can't find BaseClass in src/test/java and so would use the parent class loader to load it.
URLClassLoader classLoader = URLClassLoader.newInstance(
new URL[] { new File("src/test/java").toURI().toURL() },
BaseClass.class.getClassLoader()
);
I would like to find or create a Java class loader that loads only system classes, excluding any classes on the application defined class path. My goal is to use that class loader to build a class loader that loads from a specific JAR file, resolving system classes using the system-only class loader, but not contaminating the classes from the JAR file with any classes defined by my application.
So far I have not found any way to create a system-only class loader that does not either use private APIs or make assumptions about the Java implementation. See code below that works in my current environment but makes undesirable assumptions.
If there is no way to create an implementation independent system-only class loader, is there a reason why not? Is what I am trying to do a bad idea?
private ClassLoader createJarClassLoader(File f)
{
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
if (systemClassLoader instanceof URLClassLoader) {
URLClassLoader cl = (URLClassLoader) systemClassLoader;
URL[] urls = cl.getURLs();
List<URL> wantedURLs = new ArrayList<>();
for (URL u : urls) {
if (isSystemURL(u)) {
wantedURLs.add(u);
}
}
try {
wantedURLs.add(f.toURI().toURL());
} catch (MalformedURLException ex) {
return null;
}
return new URLClassLoader(wantedURLs.toArray(new URL[0]), null);
}
return null;
}
private boolean isSystemURL(URL u)
{
return u.getPath().contains("/jre/");
}
You need to set the parent ClassLoader to be the bootstrap ClassLoader. Call getClassLoader() on a java.lang.String object to get the bootstrap ClassLoader and use that in your ClassLoader constructor.
public class MyClassLoader extends URLClassLoader {
protected MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
static MyClassLoader getInstance(URL[] urls) {
ClassLoader parent = "".getClass().getClassLoader();
return (new MyClassLoader(urls, parent));
}
}
The representation of the bootstrap classloader is documented as implementation dependent
Some implementations may use null to represent the bootstrap class loader. This method [getClassLoader] will return null in such implementations if this class was loaded by the bootstrap class loader.
but the same wording applies to parent ClassLoader in the ClassLoader constructor, which means that this solution is portable.
I've compiled at runtime a java file on file system which is a very simple class:
public class Test {
public static int res(int a, int b) {
return a*b;
}
}
now I just want to call res method from my project (that will be a jar), but my code produce java.lang.ClassNotFoundException: Test
and this is how I've loaded the class:
URL[] urls = new URL[] {new URL("file:///"+UtilOverriding.getFile1().replace("java", "class"))};
URLClassLoader loader = new URLClassLoader(urls);
Class clazz = loader.loadClass("Test");
When you specify a class path you have to provide the directory which is the parent to all your packages. Try instead.
new URL("file:///"+UtilOverriding.getFile1().getParent()}
I'm trying to test that a class can be loaded to the jvm more then once but using different ClassLoader
so my code tries to load a class (class name "Tzvika") twice
first using the default ClassLoader
and in the second try using the URLClassLoader
the problem is that i get the same reference for the URLClassLoader and the default ClassLoader
what i'm doing wrong?
here code:
public static void main(String[] args) {
Tzvika t1 = new Tzvika();
System.out.println("t1 class loader: " + t1.getClass().getClassLoader());
Tzvika t2 = null;
try {
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/C://data/workspace/ashrait/tests/SampleClassLoader/bin/com/tzvika/sample/")});
// same problem when i do this
//URLClassLoader clsLoader = new URLClassLoader(new URL[] {new URL("file:/C://data/workspace/ashrait/tests/SampleClassLoader/bin/com/tzvika/sample/")});
Class cls = clsLoader.loadClass("com.tzvika.sample.Tzvika");
t2 = (Tzvika)cls.newInstance();
System.out.println("t2 class loader: " + t2.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
here my console output:
t1 class loader: sun.misc.Launcher$AppClassLoader#1a52fdf
t2 class loader: sun.misc.Launcher$AppClassLoader#1a52fdf
Create the URLClassLoader without a parent.
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/C://data/workspace/ashrait/tests/SampleClassLoader/bin/com/tzvika/sample/")}, null);
Note also that you should specify the path to the root of the classpath, ie.
URLClassLoader clsLoader = URLClassLoader.newInstance(new URL[] {new URL("file:/C://data/workspace/ashrait/tests/SampleClassLoader/bin")});
To better clarify, the constructor of URLClassLoader states
The URLs will be searched in the order specified for classes and
resources after first searching in the specified parent class loader.
So when you try to load a class from this ClassLoader, it will first check the parent ClassLoader, which is the bootstrap classloader, for the existence of that class. Since that parent class loader has already loaded a com.tzvika.sample.Tzvika class, it will return it.
Now, since the classes loaded by the parent class loader and your clsLoader are different, the instances of these classes therefore belong to different classes. So although you have com.tzvika.sample.Tzvika from both ClassLoaders, they are very much different.
That's the whole point of different ClassLoaders.
In another answer, I wrote the following code (Java 7):
public final class Foo
{
public static void main(final String... args)
{
final ClassLoader loader = Foo.class.getClassLoader();
final URL[] urLs = ((URLClassLoader) loader).getURLs();
for (final URL url : urLs) {
System.out.println(url);
System.out.println(Files.probeContentType(Paths.get(url.toURI())));
}
}
}
I had to cast the ClassLoader to URLClassLoader in order to have the .getURLs() method.
This is not the first time I've been doing it, and this cast has never failed for me.
Looking at the type hierarchy, I see that:
URLClassLoader -> SecureClassLoader -> ClassLoader
My question is why doesn't ClassLoader define .getURLs()? Are there concrete examples of class loaders being unable to return URLs?
Just search through grepcode. For example JBoss' ModuleClassLoader does not extend URLClassLoader