Loading .class files via URLClassLoader - NoClassDefFoundError - java

I'm aware this question has been asked multiple times (such as here), but none of the answers appear to work for me.
This is a homework assignment, I'm supposed to "hack" several class files via the reflection API, but I can't even get them to load.
There are three .class files (Inscription.class, Decoder.class, Safe.class) I put in D:\class\. Then I try to load them via an URLClassLoader:
public void Load() throws MalformedURLException {
ClassLoader loader = this.getClass().getClassLoader();
File classFolder = new File("D://class//");
// classFolder.exists() returns true at this point.
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{classFolder.toURI().toURL()},loader);
// urlClassLoader.classes is empty at this point, which is suspicous
urlClassLoader.loadClass("Safe");
// throws NoClassDefFoundError (wrong name: ea_6_1/Safe)
// Interestingly, it seems to find a package name (ea_6_1)
urlClassLoader.loadClass("ea_6_1.Safe");
// throws ClassNotFoundException
}
I also tried to load the files one by one, but apparently this shouldn't work, since URLClassLoader only accepts directories or JAR files:
URL inscription = loader.getResource("Inscription.class");
URL safe = loader.getResource("Safe.class");
URL decoder = loader.getResource("Decoder.class");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{inscription, safe, decoder});
// Exact same behavior as above.
My classpath configuration looks like this:
Is this a configuration issue or am I using the URLClassLoader wrong? Is there maybe another way of loading class files?

It appears someone has moved .class files around, without preserving the required directory structure.
A Java class declared with package ea_6_1; must reside in a directory named ea_6_1 (in every Java implementation I know of, at least).

Related

Wrong name error when class loaded from random dir

I want to make a instance of .class file located into random directory. I tried this
private final String CLASS_FOLDER =
"C:\\Users\\test\\Desktop\\fix\\core\\src\\test\\org\\poc\\";
private Class getClassFromFile(String fullClassName) throws Exception {
URLClassLoader loader = new URLClassLoader(new URL[] {
new URL("file://" + CLASS_FOLDER)
});
return loader.loadClass("Order");
}
When I run the code I get error:
java.lang.NoClassDefFoundError: Order (wrong name: com/solutions/backend/toms/actions/Order)
Looks like a security check for correct package name. Is there nay way to skip this check because I need to load .class files into random directories?
Java classes need to be in a directory hierarchy that matches their package. You cannot drop a Java .class in a "random" directory, that's simply not how classloading works.

Given only the full path to a .class file, how can I load its Class object?

I'm working on an application that needs to be able to do some analysis on Java code; some through the text and some through reflection. The user should be able to input a directory and the program will pull all .java and .class files from it. I have no problem with the .java files but when I try getting the class of the .class file it doesn't work.
I've looked at using ClassLoader with URL. What I've been able to find so far has given me this
URL url = this.openFile().toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(this.path);
return cls;
path is just a string containing the actual path of the .class file in question, e.g. Users/me/Documents/Application/out/production/MyPackage/MyClass.class. From what I understand from my own reading, this method ties me to knowing the package structure of the input, but in general I don't. All I have is the absolute path of the .class file. Is there a way, just using this path (or some simple transformation of it) that I can load into my program the actual MyClass class object and start doing reflection on it?
You have 2 options:
Use a byte code library to first read the file, so you can find out what the actual class name is.
E.g. in your example it is probably MyPackage.MyClass, but you can't know that from the fully qualified file name.
Implement your own ClassLoader subclass, and call defineClass(byte[] b, int off, int len).
Recommend using option 2.

How to list all resources without know the package name or filesystem path for jar files in Java 9

in Java 7 & 8 I use to be able to do
URLClassLoader urlLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
for (URL u : urlLoader.getURLs()) {
System.out.println("*************** url=" + url);
}
But in Java 9 the above gives an error
java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClass
Loader
So how do I list all resources without knowing the package prefix or the filesystem path of the jar files?
The Java9 Release Note states this:
The application class loader is no longer an instance of
java.net.URLClassLoader (an implementation detail that was never
specified in previous releases). Code that assumes that
ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
need to be updated. Note that Java SE and the JDK do not provide an
API for applications or libraries to dynamically augment the class
path at run-time.
To add to this from the mailing list there are few resource-lookup methods which include ways such as :
A class in a named module can read its own resources via the Class::getResourceAsStream method, which returns an InputStream. It can get a URL to one of its own resources via the Class::getResource method. These methods will not locate resources in other named modules.
The ClassLoader::getResource* methods do not locate resources in any
named modules.
All existing resource-lookup methods in Class and ClassLoader work as they do today for resources on the classpath.
The new Module::getResourceAsStream method can be
used to read the resources of any named module, without restriction.
These choices provides a balance between both resource encapsulation and readable artifact requirements on Jigsaw.
Best answer I found is to provide an argument to getResources, of course means you know prefix of the path the resources are located
ArrayList<URL> resources = new ArrayList<URL>();
ClassLoader urlLoader = ClassLoader.getSystemClassLoader();
Enumeration<URL> eU = urlLoader.getResources("com");
while (eU.hasMoreElements()) {
URL u = eU.nextElement();
JarFile jarFile = new JarFile(u.getFile().replace("file:/", "").replaceAll("!.*$", ""));
Enumeration<JarEntry> e = jarFile.entries();
while (e.hasMoreElements()) {
JarEntry jarEntry = e.nextElement();
resources.add(urlLoader.getResource(jarEntry.getName()));
}
}
Because of the move to modular architecture, the old approach for looking up class paths is no longer supported:
The application class loader is no longer an instance of
URLClassLoader but, rather, of an internal class. It is the default
loader for classes in modules that are neither Java SE nor JDK
modules.
https://docs.oracle.com/javase/9/migrate/toc.htm
You can try to parse the classpath property directly or try to do something with modules using ModuleFinder and friends.

ClassNotFound With URLClassLoader

I currently have the following directory tree structure:
CLASSES
-> ClassOne
->package
-> my
->App.class
I would like to load the App.class from my local drive. I looked around, particularly stackoverflow, and most seem to suggest that I should use the URLClassLoader.
In order to do this, I used this following code:
However, I get a ClassNotFoundError. Can anybody help me please.
String url = "file://" + classOneFolder.getAbsolutePath(); //Where classesFolder is a File representing the ClassOne directory
URL[] urls = {new URL(url)};
urlClassLoader = URLClassLoader.newInstance(urls);
//class loader needs the fully classified class name. Therefore:
Class appClass = urlClassLoader.loadClass("package.my.App");
I would suggest you use classOneFolder.toURI().toURL() instead of building the URL yourself as a String and then recreate a URL from it. On some systems (like Windows) you need to add another slash in front on the absolute filename for a valid URL. Using File.toURI().toURL() should always build a correct URL.

How can I know at runtime if a JAR file is already in the classpath?

What is the best way to know at runtime if a particular JAR file is already in my classpath ? (if that is not the case I should add it at runtime).
I do not know in advance the name of the jar nor the classes in it. A user can select it. The jar represents a runtime plugable component (a driver in my problem).
String classpath = System.getProperty("java.class.path")
this will give you what is on your classpath. you can then parse that for the jar file you want
A pragmatic way: Class.forName("com.myclass") where com.myclass is a class that is inside (and only inside) your target jar; if that throws a ClassNotFoundException, then the jar is not on you current classpath.
Bear in mind, though, that loading a jar at runtime is not very simple, you need to mess with classloaders.
As a rule (there are exceptions) that's not the way, you should be able to add explicitly the jar to the classpath before running.
Update: the updated question states that we don't know in advance "the name of the jar nor the classes in it"; if so, this answer obviously does not apply. And the answer will depend on your specific classloader. In the usual scenario, AlexAndas' answer should work.
try using this method:
public static void isJarExist(String jarName)
{
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
if (classLoader instanceof URLClassLoader)
{
URLClassLoader classLoader2 = (URLClassLoader) classLoader;
URL [] urls = classLoader2.getURLs();
for (URL url : urls)
{
File file = new File(url.getFile());
if (file.getPath().endsWith(jarName))
{
System.out.println(jarName + " exist");
return;
}
}
System.out.println(jarName + " not exist");
}
}
just pass your jar like isJarExist("myjar.jar), you can also modify it to return boolean as you wish
You can't do that in general since there might be classloaders in your application that don't offer information of the jar-files.
In the case of URLClassLoaders, you can use the solution of Alex Adas

Categories