Android Am I using multiple ClassLoader/PathClassLoaders? - java

I am having issues with ClassLoaders in Android. At least I think it has to do with ClassLoaders. So here is the issue... I have a project that utilizes a custom widget library, lets call it CustomDialogView. It is a subclass of an android View object. I have a dialog that I created that extends CustomDialogView... So its important to mention the structure of how views are created and setup within this process (and eventually the activity).
My project is an Android service that registers Views to another process via a ContentResolver. The other process then pulls this data and will create the views based off of the classes sent. So, the view is eventually created and inflated in this other process (in an activity). I know, that is confusing but that is the method that is used for our application. So, in the view code eventually I may need to show a dialog. They give me a standard Object and it uses reflection to call the show dialog method. So here is the code snippet:
private static void showTheDialog(Object mainActivity, CustomDialogView view, boolean isModal) {
try {
mainActivity.getClass().getMethod("showDialog", View.class, Boolean.TYPE).invoke(mainActivity, view, isModal);
} catch (Exception var4) {
Log.w(TAG, var4.getClass().getName(), var4);
}
}
So on their end, they have this method within that object of interest:
public void showDialog(View dialogView, boolean isModal)
So here is where the issue comes in. In the method mentioned above (showDialog). They try to cast the dialogView, into a CustomDialogView so:
CustomDialogView dialogContent = (CustomDialogView)dialogView;
I get a ClassCastException as a result. I confirmed that I am using the same version of library that implements the CustomDialogView between both projects. I print the class loaders when I call showTheDialog and I get:
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.this.is.project.myproject-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.this.is.project.myproject-1, /vendor/lib, /system/lib]]]
I print out the class loader in the showDialog (in thier project) and I get:
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.this.is.thier.project.thierproject-1.apk", zip file "/data/data/com.this.is.thier.project/code_cache/secondary-dexes/com.this.is.thier.project.thierproject-1.apk.classes2.zip", zip file "/data/data/com.this.is.thier.project/code_cache/secondary-dexes/com.this.is.thier.project.thierproject-1.apk.classes3.zip", zip file "/data/data/com.this.is.thier.project/code_cache/secondary-dexes/com.this.is.thier.project.thierproject-1.apk.classes4.zip", zip file "/data/data/com.this.is.thier.project/code_cache/secondary-dexes/com.this.is.thier.project.thierproject-1.apk.classes5.zip", zip file "/data/data/com.this.is.thier.project/code_cache/secondary-dexes/com.this.is.thier.project.thierproject-1.apk.classes6.zip"],nativeLibraryDirectories=[/data/app-lib/com.this.is.thier.project.thierproject-1, /vendor/lib, /system/lib]]]
So, we are using two different class loaders right? I have a hard time understanding this concept. Please let me know if you have any other questions if needed.

So, it appears that I am using multiple class loaders. As you may know, this can cause the class cast exception to occur. To solve, I can use reflection on the passed object. So here is a small snippit:
obj.getClass().getMethod("getterMethod").invoke(obj);
Above is the example of taking in the passed object (obj), using reflection to obtain its class then calling the method that I need. I hope this helps others. No class loader issues now. Thanks.

Related

How to load a java class outside the classpath?

I have a program where I want the user to be able to choose a .java class file from the file system, and then have that class loaded into the program.
I'm using a JFileChooser to allow the user to select a file. Then, I tried converting that file to a URL, and using a URLClassLoader to load the class (as suggested by these answers).
The problem is that, when I want to use the loadClass() method, I don't know the "full class name" of the class (e.g. java.lang.String). So, I don't know how to make this method work. Is there a way to get this class name? Or is there another way to do this?
Here is a sample of my code:
// Open the file chooser
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
File obtainedFile = fileChooser.getSelectedFile();
// Create the class loader from the file
URL classPath = obtainedFile.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] {classPath});
// Get the class from the loader
Class<?> theClassIWant = loader.loadClass("the file name"); // What do I put here??
Load a single class file is generally completely useless. Said class file isn't alone; it has more class files that are relevant. Even if you think 'nah, there is just one source file, do not worry about this', note that a single java file can easily generate multiple class files.
Thus, two options:
Don't load class files. Load jar files.
Use the usual mechanisms (META-INF/services or META-INF/MANIFEST.MF) to put some sort of class name in there so you know what to load. Then create a new classloader with the provided jar, load the manifest, figure out the main class, load that, and run it.
Attempt to determine the 'root' for the loaded class file and include that on the classpath.
This is quite difficult - the problem is, to 'load' a class file you need to tell the loader what the fully qualified name is of that class before it is loaded. But how do you know the fully qualified name? You can surmise the class name from the file (not quite always true, but usually), but the package is a more difficult issue.
You can open the class file yourself as a binary stream and write a basic class file format parser to get the fully qualified class name. Easy for an experienced java programmer. Quite tricky for someone new to java (which I gather you are, if you think this is a good idea).
You can also use existing tools to do this, such as bytebuddy or asm.
Finally, you can try a spaghetti-at-the-wall method: Keep travelling up the directory until it works. You know it isn't working if exceptions occur.
For example, to load C:\MyDir\Whatever\com\foo\MyApp.class, You first try creating a new classloader (see the API of URLClassLoader which is part of core java) using as root dir C:\MyDir\Whatever\com\foo, and then you ask it to load class MyApp.
If that works, great (but usually trying to load package-less classes is simply a non-starter, you're not supposed to do that, the CL API probably doesn't support it, intentionally, there is no fixing that).
If it doesn't, instead try C:\MyDir\Whatever\com, and load class foo.MyApp. If that doesn't work, try C:\MyDir\Whatever and load class com.foo.MyApp, and so on.
The considerable advantage is, if there is another class sitting right next to MyApp.class, and MyApp needs it, this will work fine.
You'll need to write a while loop (traversing the path structure using Paths.get and p.getParent()), catch the right exception, manipulate the path into the class name (using .replace and +), and, of course, create a class loader (URLClassLoader), load classes with it (invoke loadClass), and if you intend on running it, something like thatClass.getConstructor().newInstance() and then thatClass.getMethod("someMethod", String.class, /* all the other args here */).invoke(theInstanceYouJustMade, "param1", /*all other params */) to actually 'run' it, more to be found in the java.lang.reflect package.

Class.forName doesn't find a loaded class

I am experimenting with some cheats made for a Java game. A specific cheat blew my mind. I noticed that all the names of the classes it loads start with an exclamation mark so I decided to try and get one of those classes by name. The cheat crashes if you forget to delete the settings file it creates so I got the name of one of them - "!M". I decided that I'd inject a jar file in the game myself and experiment with that class. I created a JAR loader in C and the JAR itself. I used Cheat Engine to verify that the class itself is loaded in the JVM. I created a simple JAR which called this code upon injection:
try {
Class<?> cheatClass = Class.forName("!M");
showMSGBox(cheatClass.getName());
showMSGBox(cheatClass.getSuperclass().getName());
} catch (Exception ex) {
showMSGBox(ex.toString());
}
Where showMSGBox(string) simply shows a JOptionPane. When I injected the JAR in the game using the loader I made, I was granted with an error which stated that there is no such a class. I opened Cheat Engine again and saw the class is still there. Any ideas why this could happen and what causes it?

NullPointer when getting resource as stream

I am trying to access a file from my project's directory, let's call it
src/main/project/foo/bar.txt
In class MyClass
But when I do:
InputStream is = MyClass.class.getResourceAsStream("bar.txt");
And do anything with the InputStream, e.g.
is.toString();
I get a NullPointerException
I have also tried:
MyClass.class.getResourceAsStream("/bar.txt");
and
MyClass.class.getResourceAsStream("./bar.txt");
Is there something extra I need to do as the file is not in the same package as the class in which I'm calling it? This application is deployed on TomCat, so absolute references to the filepath are impractical.
EDIT: To the person who flagged it as a duplicate of nullpointer, I know what a nullpointer is, obviously it can't find the resource. As to why that is the case, I need help figuring out.
if your class is at src/main/project/foo/MyClass.java MyClass.class.getResourceAsStream("bar.txt"); should be fine.
if your resource is in a different folder within the project than the class use:
MyClass.class.getClassLoader().getResource("main/project/foo/bar.txt");
in general:
MyClass.class.getClassLoader().getResource("package/<subpackage/>filename");
To my knowledge this works with any class anywhere within the same project.
MyClass.class.getResourceAsStream(); operates relative to the Location of myClass and you can't change that.

Registering multiple .dll libraries into a single java class using JNA

First of all some context, to thoroughly explain the methods I've already tried:
I'm working in a java-based programming platform on windows which provides access to custom java functions with several other extensions. Within the source code of this modelling platform, there is a class "CVODE" which grants access to native library "cvode" to import the functionality of a C++ library CVODE.
//imports
public class CVODE {
static {
Native.register("cvode");
}
public static native int ... //methods
}
I created shared libraries from the CVODE library, which resulted in 2 files: sundials_cvode.dll and sundials_nvecserial.dll.
Adding the first library to my java path obviously resulted in
Unexpected Exception UnsatisfiedLinkError: Unable to load library 'cvode': The specified module could not be found.
as the names were not compatible. Therefore I changed the name of sundials_cvode.dll to cvode.dll and retried. Resulting in an error indicating that not all methods are present in the library sundials_cvode.dll:
Unexpected Exception UnsatisfiedLinkError: Error looking up function 'N_VDestroy_Serial': The specified procedure could not be found.
This convinces me that the library is being found and loaded correctly, but not all methods are available. Examining the dll's in question led me to the conclusion that the CVODE class requires functions from both the sundials_cvode.dll and sundials_nvecserial.dll libraries. Therefore I tried changing the platform source-code to
public class CVODE {
static {
Native.register("sundials_cvode");
Native.register("sundials_nvecserial");
}
public static native int ... //methods
}
which still results in
Unexpected Exception UnsatisfiedLinkError: Error looking up function 'N_VNew_Serial': The specified procedure could not be found.
I have confirmed this method is present in both the class file and in the dll:
So I can only guess the error results from calling the Native.register() twice. resulting in the 2nd library not being loaded or an error down the way. I'd appreciate some insight in what I'm doing wrong or how I can gain a better overview of what's going wrong.
As far as I know, you can only load one dll per class, i.e. split the classes into two, each providing the methods the particular dll provides.
See also here: https://stackoverflow.com/a/32630857/1274747

Access classes from package

I'm developing an android test app and i'm going to access all internal class of android.view package. android.view is a package that is present in jar file. I tried by loading package name but it doesn't display the classes if any one tried
this already, please help.
Here's what I tried so far:
public static void main() throws ClassNotFoundException{
Class o =Class.forName("android.view");
Class[] C=o.getDeclaredClasses();
for(int i=0;i<C.length;i++) {
Classname = C[i].getName();
ClassesDisplayActivity.your_array_list3.add(Classname);
Log.i("Ramu","classname "+ C[i].getName());
}
}
}
It is not possible to determine at runtime all of the classes that are in a package using a standard class loader.
You might have some luck with this library though:
https://code.google.com/p/reflections/
Package is not a class. You cannot call Class.forName() for package and access classes that belong to class using getDelcaredClasses().
I do not know what do you really need, so I'd recommend you to explain this in separate question. probably you will receive better solutions.
However if you really need this you have to do the following:
Get your classpath by calling System.getProperty(java.class.path)
split this property to its elements by colon
iterate over the list and read each resource. If resource is jar you can use ZipInputStream, if it is a directory use File class.
filter list of resources you got at #3.
Fortunately you can use 3rd party library named Reflections that helps you to do all this without writing code.

Categories