I have a requirement where i have to dynamically load java program based on the input. All java class files are placed in folder : C://Users/me/workspace/File/bin/Encrypt/.
there are 3 class files: Class1.class, Class2.class, Class3.class in this folder
To pick them up in runtime i am using below code:
package First;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class Main
{
public static void main(String[] args) {
// Say Class.class is the input file to be picked up.
String abc = "Class1.class";
try {
File file = new File("C:\\Users\\me\\workspace\\File\\bin\\Encrypt");
//convert the file to URL format
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
//load this folder into Class loader
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(abc);
System.out.println("cls.getName() = " + cls.getName());
cls.encrypt();
}
catch ( ClassNotFoundException | MalformedURLException e) {
e.printStackTrace();
}
}
}
I am facing below issues here:
The above code shows the error: .NoClassDefFoundError: Class1??
have used all types of - / ,\ ,//,\ in the path.
How can i call a method sum() in Class1 file ??
since you have ...\bin\Encrypt is the folder where you classes are i would bet the package for your classes is Encrypt. in this case abc should be
String abc = "Enrcypt.Class1";
if the classes are in the default package which means they have no package declaration than use
String abc = "Class1";
note also that there is no need for the extension .class in the name of the class.
To call the a method of class loaded this way you should refer to java refelction api.
first get a Method object from Class cl using getMethod
than use that method object's method invoke to call the function. you'll need to provide an instance of the Class dynamically loaded if the the method to call is not static, if it is static just pass null.
Related
I am trying to delete a DLL which has been loaded into JNA and later disposed. I have tried all the solutions described in the answer to this question, but they are not working: How to dispose library loaded with JNA
Here is code I've tried without a time delay:
import java.io.File;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
class Filter {
private static ExtDLLTool DLLUtil;
final private static String dllPath = "./ExternalDownloader_64.dll";
static {
DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class);
}
public static void main(String[] args) {
if (DLLUtil != null) {
DLLUtil = null;
NativeLibrary lib = NativeLibrary.getInstance(dllPath);
lib.dispose();
}
File dllFile = new File(dllPath);
if(dllFile.exists()){
boolean isDeleted = dllFile.delete();
if(!isDeleted){
System.out.println("Unable to delete dll file, since it hold by jvm");
}
}
}
private interface ExtDLLTool extends Library {
String validateNomination(String dloadProps);
}
}
I added a time delay to give the native code time to release the handle:
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
class Filter {
private static ExtDLLTool DLLUtil;
final private static String dllPath = "./ExternalDownloader_64.dll";
static {
DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class);
}
public static void main(String[] args) throws Exception{
if (DLLUtil != null) {
DLLUtil = null;
NativeLibrary lib = NativeLibrary.getInstance(dllPath);
lib.dispose();
Thread.sleep(3000);
}
File dllFile = new File(dllPath);
if(dllFile.exists()){
Files.delete(Paths.get(dllPath));
// boolean isDeleted = dllFile.delete();
if(dllFile.exists()){
System.out.println("Unable to delete dll file, since it hold by jvm");
}
}
}
private interface ExtDLLTool extends Library {
String validateNomination(String dloadProps);
}
}
This code results in an exception implying the JVM has not released the file.
Exception in thread "main" java.nio.file.AccessDeniedException: .\ExternalDownloader_64.dll at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
In the end the problem is, that Native#open is called twice and Native#close only once. The assumption behind the presented code is, that:
NativeLibrary lib = NativeLibrary.getInstance(dllPath);
yields the same NativeLibrary instance, that is used by:
DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class);
This assumption does not hold. Indeed NativeLibrary#load does use caching and if invoked with the same parameters it will yield only a single instance.
The codepath behind Native.loadLibrary passes two options to Native#loadLibrary: calling-convention and classloader. The calling-convention is equal to the default calling convention, so can be ignored. It is/would be automatically added in NativeLibrary#getInstance. The classloader though is not set to a default value and there is the difference. The options are part of the caching key and thus a second instance of the NativeLibrary is created and not the first returned.
To make it work, the call to NativeLibrary#getInstance must pass the correct classloader. If you modify the sample like this:
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
class Filter {
private static ExtDLLTool DLLUtil;
final private static String dllPath = "./ExternalDownloader_64.dll";
static {
DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class);
}
public static void main(String[] args) throws Exception{
if (DLLUtil != null) {
DLLUtil = null;
NativeLibrary lib = NativeLibrary.getInstance(dllPath, ExtDLLTool.class.getClassLoader());
lib.dispose();
Thread.sleep(3000);
}
File dllFile = new File(dllPath);
if(dllFile.exists()){
Files.delete(Paths.get(dllPath));
// boolean isDeleted = dllFile.delete();
if(dllFile.exists()){
System.out.println("Unable to delete dll file, since it hold by jvm");
}
}
}
private interface ExtDLLTool extends Library {
String validateNomination(String dloadProps);
}
}
it works as expected.
After discussion there is another requirement: The cache path is only hit in a limited number of cases:
the library name is the filename of the library (without a prefix)
the library name is the absolute path to the library
the library name is the "base" name without any prefixes or suffixes the default library search mechanism adds (on windows ".dll" should be stripped, on linux "lib" prefix and ".so" suffix should be stripped) (UNTESTED!)
The TL;DR version: find the absolute path name and use that for interface loading and NativeLibrary loading.
I was able to reproduce the problem with your code, but only on Windows. When reproducible, I was able to successfully delete the file by adding a garbage collection suggestion before the time delay:
if (DLLUtil != null) {
DLLUtil = null;
NativeLibrary lib = NativeLibrary.getInstance(dllPath);
lib.close();
System.gc();
System.gc();
Thread.sleep(3000);
}
When JNA loads a Windows DLL via Native.loadLibrary(), it internally executes the WinAPI LoadLibraryExW function.
Internally the Java instance is stored in a map to be re-used when possible -- however for this to happen, it requires two things to look up the same Java object:
the DLL Path must be an absolute path
the options must match. In this case, you would need to pass the classloader as an argument as Matthias Bläsing indicated in his answer:
// if loaded like this:
DLLUtil = (ExtDLLTool) Native.loadLibrary(dllPath, ExtDLLTool.class);
// fetch from cache like this:
NativeLibrary lib = NativeLibrary.getInstance(dllPath, ExtDLLTool.class.getClassLoader());
lib.dispose();
This should allow you to delete the file.
However, in your case, with the relative path, the library is getting unloaded but the old java object isn't getting closed until GC occurs.
The dispose() (or close() as of 5.12) call in JNA eventually calls the Native.close() method which uses the Windows API FreeLibrary function. This unloads the DLL from the Process memory, so the advice on the linked question on how to dispose is still accurate in the case that you want to re-load the library. If you're not reloading the library, using dispose() (5.11-) or close() (5.12+) is optional.
If you must use a relative path, consider this approach using a PhantomReference inspired by this answer to track the deletion:
if (DLLUtil != null) {
// Unload the DLL from process memory
// Optional here, as it will be called by a cleaner on GC below
NativeLibrary lib = NativeLibrary.getInstance(dllPath);
lib.close();
System.out.println("Closed.");
// Remove any internal JVM references to the file
final ReferenceQueue rq = new ReferenceQueue();
final PhantomReference phantom = new PhantomReference(DLLUtil, rq);
DLLUtil = null;
// Poll until GC removes the reference
int count = 0;
while (rq.poll() == null) {
System.out.println("Waiting...");
Thread.sleep(1000);
if (++count > 4) {
// After 5 seconds prompt for GC!
System.out.println("Suggesting GC...");
System.gc();
}
}
System.out.println("Collected.");
}
The DLL was successfully deleted following this sequence. It did take a second GC call to take effect:
Closed.
Waiting...
Waiting...
Waiting...
Waiting...
Waiting...
Suggesting GC...
Waiting...
Suggesting GC...
Collected.
Deleted!
My objective is to look at some lines of codes of an external file and count the number of functions of a class are called then.
For example, if I have the following code:
import java.io.BufferedReader;
import whatever.MyClass;
import java.util.ArrayList;
...
...
public void example(){
InputStreamReader isr = new InputStreamReader (whatever);
MyClass object = new MyClass();
someArrayList.add(whatever2)
someArrayList.add(whatever3)
}
In this case, BufferedReader and MyClass functions were called once, and ArrayList functions were called twice.
My solution for that is get a list of all methods inside the used classes and try to match with some string of my code.
For classes created in my project, I can do the following:
jar -tf jarPath
which returns me the list of classes inside a JAR . And doing:
javap -cp jarPath className
I can get a list of all methods inside a JAR whit a specific class name. However, what can I do to get a external methods names, like add(...) of an "external" class java.util.ArrayList?
I can't access the .jar file of java.util.ArrayList correct? Anyone have another suggestion to reach the objective?
The compiler doesn't put the imports into the object file. It throws them away. Import is just a shorthand to the compiler.(Imports are a compile-time feature ).
first step :
use Qdox https://github.com/paul-hammant/qdox to get all the imports in a class :
String fileFullPath = "Your\\java\\ file \\full\\path";
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSource(new FileReader( fileFullPath ));
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
System.out.println(imp);
}
second step :
inspire from that code , loop through your imports (String array) and apply the same code and you will get the methods .
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Tes {
public static void main(String[] args) {
Class c;
try {
c = Class.forName("java.util.ArrayList");
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output :
public void java.util.ArrayList.add(int,java.lang.Object)
public boolean java.util.ArrayList.add(java.lang.Object)
public boolean java.util.ArrayList.remove(java.lang.Object)
public java.lang.Object java.util.ArrayList.remove(int)
public java.lang.Object java.util.ArrayList.get(int)
public java.lang.Object java.util.ArrayList.clone()
public int java.util.ArrayList.indexOf(java.lang.Object)
public void java.util.ArrayList.clear()
.
.
.
All the code - one class :
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaSource;
public class Tester {
public static void main(String[] args) {
// put your .java file path
// CyclicB is a class within another project in my pc
String fileFullPath =
"C:\\Users\\OUSSEMA\\Desktop\\dev\\OCP_Preparation\\src\\w\\CyclicB.java";
JavaDocBuilder builder = new JavaDocBuilder();
try {
builder.addSource(new FileReader( fileFullPath ));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
Class c;
try {
c = Class.forName(imp);
Arrays.stream(getAccessibleMethods(c)).
forEach(System.out::println);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static Method[] getAccessibleMethods(Class clazz) {
List<Method> result = new ArrayList<Method>();
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
result.add(method);
}
clazz = clazz.getSuperclass();
}
return result.toArray(new Method[result.size()]);
}
}
Output all the methods within the classes imported in the file CyclicB.java :
private void java.lang.Throwable.printStackTrace(java.lang.Throwable$PrintStreamOrWriter)
public void java.lang.Throwable.printStackTrace(java.io.PrintStream)
public void java.lang.Throwable.printStackTrace()
public void java.lang.Throwable.printStackTrace(java.io.PrintWriter)
public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
.
.
.
You may look into OpenJDK project that has a Java compiler. Learn to build the modified versions. Investigate the syntax analysis layer of this compiler and find where the method calls are handled. Put the logging into these locations and now you only need to build your java file with the modified compiler to get the information about the calls.
The build is complex, but you will likely only need a careful editing in a few files. It is not exactly very low hanging fruit but I think it should be possible to discover these files and make changes in them, and still may be a simpler/cleaner approach than to implement the own Java syntax parser (also doable with JavaCC).
If you also need to track calls from the external libraries, build them with the modified compiler as well and you will have the needed records.
GNU Classpath is another open source project where you can do the similar thing, and it may be easier to build. However, unlike OpenJDK, GNU Classpath java system library is not complete.
This approach may not discover some methods called during reflection. But it would discover that reflection framework methods have been called. If it is a security - related project, the simplest would be to agree that reflection is not allowed. It is uncommon to use reflection in a normal Java application that is not a framework.
I need a way to run a java method for ex. createModule("Login")
and as an output to have:
New folder named mod_login
Inside on mod_login java classes files created from a template
If the template is
class Name extends Blah implement Blah {
private createdInt;
private int getCreatedInt() {
return createdInt;
}
}
In return I want to get a dynamically created class:
class Login extends Blah implement Blah {
private loginInt;
private int getLoginInt() {
return loginInt;
}
}
Tried to look into groovy to do it, but could not find anything usefull.
P.S. it shouldn't happen on runtime, it's a more like a helper to instantiate these modules with just 1 button, instead of typing them
Working example which will help you.
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
public class HelloWorld {
public static void main(String[] args) throws Exception {
// create an empty source file
File sourceFile = File.createTempFile("Hello", ".java");
sourceFile.deleteOnExit();
// generate the source code, using the source filename as the class name
String classname = sourceFile.getName().split("\\.")[0];
String sourceCode = "public class " + classname + "{ public void hello() { System.out.print(\"Hello world\");}}";
// write the source code into the source file
FileWriter writer = new FileWriter(sourceFile);
writer.write(sourceCode);
writer.close();
// compile the source file
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
File parentDirectory = sourceFile.getParentFile();
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(parentDirectory));
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
fileManager.close();
// load the compiled class
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { parentDirectory.toURI().toURL() });
Class<?> helloClass = classLoader.loadClass(classname);
// call a method on the loaded class
Method helloMethod = helloClass.getDeclaredMethod("hello");
helloMethod.invoke(helloClass.newInstance());
}
}
You just need 2 variables defined in a method which creates files dynamically.
className
&
propertyName
Now use these to create a file with .java extension and write the text from your template as is. For className and propertyName generation logic use the variables that you have above.
If you want to create more than one such files then take className & propertyName in a list and run a forloop.
The code is as follows
what it does is it loads all the classes inside a jar file which I placed inside my home directory .
import java.io.File;
import java.util.jar.JarFile;
import java.util.jar.JarEntry;
import java.net.URLClassLoader;
import java.net.URL;
import java.util.Enumeration;
import java.lang.ClassLoader;
public class Plugin extends ClassLoader {
public static void main(String[] args) throws Exception {
File file = new File(System.getProperty("user.home") + "/HelloWorld.jar");
URLClassLoader clazzLoader = URLClassLoader.newInstance(new URL[]{file.toURI().toURL()});
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry element = entries.nextElement();
if (element.getName().endsWith(".class")) {
try {
Class c = clazzLoader.loadClass(element.getName().replaceAll(".class", "").replaceAll("/", "."));
c.newInstance(); // this proves that class is loaded
} catch (Exception e) {
e.printStackTrace();
}
}
}
Class cls = Class.forName("HelloWorld");
cls.newInstance();
Plugin p = new Plugin();
p.checkIfLoaded();
}
public void checkIfLoaded() {
System.out.println("coming in");
if (findLoadedClass("HelloWorld") != null){
System.out.println("Yepee, HelloWorld class is loaded !");
}
}
}
My HelloWorld is as in https://github.com/HarishAtGitHub/doc/blob/master/makeExecutableJar/HelloWorld.java
and the jar is got using the instructions in my github account mentioned above .
c.newInstance() works .
How did I confirm ?
the static block got executed ...
but Class.forName("HelloWorld") throws ClassNotFoundException
also findLoadedClass("HelloWorld") is null ..
I cannot understand why this strange behaviour ?
Please guide ...
This is a classloader issue.
As per the Javadocs to Class.forName, you are looking up the class using the classloader of the current class. As your main class, this will be the JVM's bootstrap classloader (and will more or less just include the standard library plus anything you provided as a -cp command line argument). It is not going to delegate to the classloader that you instantiated as a local variable, and so will not return classes that that classloader could find.
If you were to specify the classloader explicitly, and call
Class.forName("HelloWorld", true, clazzloader)
then the classloader you just created will be searched instead and your class should be found.
Because Class.forName(String) uses currentClassLoader and you have load the class in different ClassLoader.
According with javadoc, invoking Class.forName(String) is equivalent to:
Class.forName(className, true, currentLoader)
I have a class loader that loads the "main" class from all jar files in the /plugins folder
this assumes that all jars have the package plugin.(plugin name) containing the class called main. each main class has a constructor called main.
the classes load successfully, but I need to know how to call the main constructor from the loaded class.
(this class/classes are loaded at runtime)
I have tried using this:
Constructor c = cls.getConstructor(Integer.class); //line 41
Plugin plug = (Plugin) c.newInstance(0);
but I get this error:
java.lang.NoSuchMethodException: plugin.myplugin.main.<init>(java.lang.Integer)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getConstructor(Unknown Source)
at hkr.classloader.PluginLoader.loadPlugins(PluginLoader.java:41)
at hkr.core.startup.InitializeGame.inigame(InitializeGame.java:32)
at hkr.launcher.main.LauncherMain.main(LauncherMain.java:16)
package hackers.classloader;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import org.java.plugin.Plugin;
public class PluginLoader
{
#SuppressWarnings({ "unused", "rawtypes", "resource" })
public static void loadPlugins() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Class cls = null;
int x = hackers.core.startup.InitializeGame.map.size();
for (int i = 1; i<=x;i++)
{
String className = hackers.core.startup.InitializeGame.map.get(i + "");
File file = new File(System.getProperty("user.dir") + File.separator + "plugins" + File.separator + className + ".jar");
URL url = null;
try {
url = file.toURI().toURL();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
try {
cls = cl.loadClass("plugin." + className + ".main");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Constructor c = cls.getConstructor(Integer.TYPE);
Plugin plug = (Plugin) c.newInstance(0);
}
}
}
If your constructor takes an java.lang.Integer, from what I see, your code should work.
But if your constructor's sole parameter is an int, getConstructor will fail. You have to use Integer.TYPE instead of Integer.class in that case.
I I am right, what you need to do is:
Constructor c = cls.getConstructor(Integer.TYPE);
Edit: Based on your edits and your comments, there are several problems.
The class you want to load does not seem to have any explicit constructor, which means that you simply need to do cls.getConstructor()
What you want to execute (public static void main), is a static method for which you normally don't need an instance of a class. Also, I'm not sure "main" would be a good name for the reasons explained by user #Eric B.
Since you want to call a method, You have to instantiate the constructor AND also call the method.
Based on my understanding, the code you would want to execute should be something like that:
Constructor c = cls.getConstructor(); // we get the implicit constructor without parameters
Plugin plugin = (Plugin) c.newInstance(); // we instantiate it, no parameters
Method m = cls.getDeclaredMethod("main", Integer.TYPE);
m.invoke(plugin, 0); // we invoke the method "main" on our dynamically loaded class, with the 0 parameter.