I want to load Java classes dynamically on Android application.
I have the .jar file and I can create the .dex file on that jar with command dx. Then, how can I use them to load the classes i need in an Android application ?
I would to use them like:
DynamicLoadedClassJar obj = new DynamicLoadedClassInJar()
...
DynamicLoadedClassInJar obj = (DynamicLoadedClassInJar) ...
All I have is .jar file and I can generate .dex file (but I don't know how to use them).
Can you give me a snippet of code for this? Thank you
Here is how you can load jar and call a method on any of its classes
URLClassLoader child = new URLClassLoader(jarPath.toURL(), this.getClass().getClassLoader());
Class classToLoad = Class.forName("yourPackage.yourClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);
Related
I am currently trying to extract from the jdk the preview class files such as java.lang.Record from jrt-fs.jar (in libs folder), but it does not find the preview classes such as Record when iterating over it. This is the code I am using:
Path jrtFsJar = jdk15Home.resolve("lib").resolve("jrt-fs.jar");
jrtFsJarLoader = new URLClassLoader(new URL[] {jrtFsJar.toUri().toURL()});
FileSystem jrtFs = FileSystems.newFileSystem(
URI.create("jrt:/"),
Collections.emptyMap(),
jrtFsJarLoader);
Files.walk(jrtFs.getPath("/modules")).forEach(path ->
// Here is walks over classes such as "modules/java.base/java/lang/Object.class"
// but not over "modules/java.base/java/lang/Record.class"
)
I have also tried a more direct approach:
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
// Object works, I get the bytes.
byte[] object = Files.readAllBytes(jrtFs.getPath("modules", "java.base",
"java/lang/Object.class"));
// Record fails, NoSuchFile.
byte[] object = Files.readAllBytes(jrtFs.getPath("modules", "java.base",
"java/lang/Record.class"));
Now when I'm running the exact same jdk with --enable-preview, I can use records.
How do I extract the Record class from the jdk linux/lib? Are the preview class inside of it or should I look for them elsewhere? Do I need a specific flag to access them?
Any help is appreciated.
When you are running on a JDK that has a jrt file system already, you can access the jrt file system of another JDK much easier.
FileSystem jrtFs = FileSystems.newFileSystem(
URI.create("jrt:/"), Map.of("java.home", jdk15Home.toString()));
byte[] object = Files.readAllBytes(
jrtFs.getPath("modules", "java.base", "java/lang/Record.class"));
The built-in file system will create a special class loader for the foreign JDK’s jrt-fs.jar that does not delegate to the parent loader for the classes of this jar. So it does not end up at its own implementation again.
Since it uses the implementation provided by the other JDK, it will be able to handle newer features or even JDKs using an entirely different data format for its module storage.
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.
What is Intellij doing for me that sbt isn't?
I am working on a Scala project for which I'd like to support user-defined plugin classes. I've implemented a subclass of ClassLoader:
class PluginLoader(directory: String) extends ClassLoader {
override def findClass(className: String): Class[_] = {
val bytes = loadClassData(className)
defineClass(className,bytes,0,bytes.length)
}
def loadClassData(className: String): Array[Byte] = {
val fileName = className.replace('.',File.separatorChar) + ".class"
val file = new File(directory,fileName)
val bytes = new Array[Byte](file.length.toInt)
val inputStream = new DataInputStream(new FileInputStream(file))
inputStream.readFully(bytes)
inputStream.close()
bytes
}
}
and I call this with user-defined directory and name of a pre-compiled scala class:
new PluginLoader(directory).loadClass(className).newInstance()
findClass appears to succeed at finding the class the user provides. However, it is then called again looking for scala.runtime.java8.JFunction2$mcIII$sp.class. It looks in the user-provided directory, so it cannot find the loaded class.
Here's the error I get: (run-main-0) java.io.FileNotFoundException: ./scala/runtime/java8/JFunction2$mcIII$sp.class (No such file or directory)
I have successfully run this application in Intellij, which makes me think I am missing some configuration in sbt, and Intellij is catching my mistake for me.
Do I need to do additional configuration? Should I be loading in an entire jar file instead of just passing a class file?
When you compile a class - not only Scala class, Java class as well - then it not necessarily mean that you will get a single .class file.
Internal classes, lambdas/closures, etc will result with additional bytecode, that is required to run, and which doesn't live in the same file as your "original" class.
As far as I can tell here Java created some class because it is the body of some lambda, and you did not loaded it.
Bottom line is: you'd better load everything. IntelliJ might have simply passed a whole classpath to JVM, so your classloader might not have even used your extensions (though you'd have to attach debugger in order to be sure).
In my application I load resources in this manner:
WinProcessor.class.getResource("repository").toString();
and this gives me:
file:/root/app/repository (and I replace "file:" with empty string)
This works fine when I run my application from the IDE, but when I run the jar of my application:
java -jar app.jar
The path becomes:
jar:/root/app.jar!/repository
is there any way to solve this problem?
I'll use the "repository" dir name in order to create this:
ConfigurationContext ctx = (ConfigurationContext) ConfigurationContextFactory.createConfigurationContextFromFileSystem(repositoryString, null);
In the same manner, I'll get one file name (instead of a dir) and I'll use it this way:
System.setProperty("javax.net.ssl.trustStore", fileNameString)
It sounds like you're then trying to load the resource using a FileInputStream or something like that. Don't do that: instead of calling getResource, call getResourceAsStream and read the data from that.
(You could load the resources from the URL instead, but calling getResourceAsStream is a bit more convenient.)
EDIT: Having seen your updated answer, it seems other bits of code rely on the data being in a physical single file in the file system. The answer is therefore not to bundle it in a jar file in the first place. You could check whether it's in a separate file, and if not extract it to a temporary file, but that's pretty hacky IMO.
When running code using java -jar app.jar, java uses ONLY the class path defined in the manifest of the JAR file (i.e. Class-Path attribute). If the class is in app.jar, or the class is in the class path set in the Class-Path attribute of the JAR's manifest, you can load that class using the following code snippet, where the className is the fully-qualified class name.
final String classAsPath = className.replace('.', '/') + ".class";
final InputStream input = ClassLoader.getSystemResourceAsStream( path/to/class );
Now if the class is not part of the JAR, and it isn't in the manifest's Class-Path, then the class loader won't find it. Instead, you can use the URLClassLoader, with some care to deal with differences between windows and Unix/Linux/MacOSX.
// the class to load
final String classAsPath = className.replace('.', '/') + ".class";
// the URL to the `app.jar` file (Windows and Unix/Linux/MacOSX below)
final URL url = new URL( "file", null, "///C:/Users/diffusive/app.jar" );
//final URL url = new URL( "file", null, "/Users/diffusive/app.jar" );
// create the class loader with the JAR file
final URLClassLoader urlClassLoader = new URLClassLoader( new URL[] { url } );
// grab the resource, through, this time from the `URLClassLoader` object
// rather than from the `ClassLoader` class
final InputStream input = urlClassLoader.getResourceAsStream( classAsPath );
In both examples you'll need to deal with the exceptions, and the fact that the input stream is null if the resource can't be found. Also, if you need to get the InputStream into a byte[], you can use Apache's commons IOUtils.toByteArray(...). And, if you then want a Class, you can use the class loader's defineClass(...) method, which accepts the byte[].
You can find this code in a ClassLoaderUtils class in the Diffusive source code, which you can find on SourceForge at github.com/robphilipp/diffusive
And a method to create URL for Windows and Unix/Linux/MacOSX from relative and absolute paths in RestfulDiffuserManagerResource.createJarClassPath(...)
Construct a URL, you can then load a resource (even in a jar file) using the openStream method.
I want to write a program to dynamically invoke a method inside another Java class (uncompiled) whose file name with location is given. For this I've used the following code but it wasn't working.
//folder location of my java file to be loaded
String url = "C:/Temp/testcases/test.java";
//name of the java file to be loaded
String classname = "test.java";
this.class.classLoader.rootLoader.addURL(new URL(url+str));
Class.forName(str).newInstance();
The above instance is unable to invoke the method inside the java file that I want to load dynamically. What is the error in it?
The class loader is only able to load compiled classes. It's not able to open Java source files, compile them on the fly, and load their class.
Moreover, a class name is not the same as a file name.
I agree with your answer. The error in the above code is with the new URL(C://...). Had I mentioned the package name wrt path correctly, it should have worked. Anyways I was dealing with groovy files so I found this code more efficient than the normal class.forname that I've mentioned above.
def sourceString = (new File(C:/xyz.groovy)).getText()
ClassLoader parent = getClass().getClassLoader();
GroovyClassLoader loader = new GroovyClassLoader(parent);
Class groovyClass = loader.parseClass(sourceString);
template = groovyClass.newInstance()