Java: Dynamically loading external classes - java

I am writing an application that will load Java scripts. I currently have a GUI which utilizes a JFileChooser to allow the user to select a script from their machine. The script file can be anywhere. It is not on the classpath. Having only a File object to represent that script file, how can I obtain a Class representation of it?
I know that to load a class you need its binary name, so in.this.format. However, the problem with that is I don't know how the script writer may have packaged it. For example, he/she may have, while developing it, put the script file in the package foo.bar. After I download this script and place it in my documents (i.e., not in foo/bar), I can't load the script without knowing that it was packaged in foo.bar. If the class name is Test and I try to create a URLClassLoader pointing to the script file by doing new URLClassLoader(new URL[] { new URL(scriptFile.toURI().toURL()) }) and I do classLoader.loadClass("Test") I will get an exception saying that the class had the wrong name, and the correct name is foo.bar.Test. But how am I supposed to know that ahead of time?
This is what I have right now:
public class ScriptClassLoader extends URLClassLoader {
private final File script;
public ScriptClassLoader(File script) throws MalformedURLException {
super(new URL[] { script.toURI().toURL() });
this.script = script;
}
public Class<?> load() throws ClassNotFoundException {
String fileName = script.getName();
String className = fileName.substring(0, fileName.indexOf(".class"));
return loadClass(className);
}
}
How do people load scripts at runtime that are not part of the program's classpath, and the binary name of the class is not known?

If you just need to load a class from a given .class file, no matter how this classes is named, you can load the data yourself and then call ClassLoader's defineClass() method:
RandomAccessFile raf = new RandomAccessFile(script, "r");
try {
byte[] classData = new byte[(int) raf.length()];
raf.readFully(classData);
return super.defineClass(null, classData, 0, classData.length);
} finally {
raf.close();
}

Related

Java - URI is not hierarchical error appears only in the exported JAR, not in Eclipse

I have a Java program that is able to change the wallpaper taking in input an image using WINAPI.
Everything works fine when I run it inside Eclipse IDE, but when I run the JAR I got the error:
Caused by: java.lang.IllegalArgumentException: URI is not hierarchical
public class Main {
//INIT USER32 for WINAPI
public static interface User32 extends Library {
User32 INSTANCE = (User32) Native.loadLibrary("user32",User32.class,W32APIOptions.DEFAULT_OPTIONS);
boolean SystemParametersInfo (int one, int two, String s ,int three);
}
public static void main(String[] args) throws IOException, URISyntaxException {
//Change wallpaper
System.out.println("Change wallpaper");
URL url = Main.class.getResource("/resources/img.jpg");
File f = new File(url.toURI());
String path = f.getPath();
User32.INSTANCE.SystemParametersInfo(0x0014, 0, path , 1);
}
}
The image is shipped within the JAR, so maybe the error is related to this since the program is not able to correctly read to URL inside the JAR.
Is there a way to solve this?
A jar file is just a compressed file when the resource is bundled as a jar java will be treated as a single file, which means it will not access to your resources.
try using this instead getResourceAsStream(...);

Java opening File Streams in one class and closing/deletion of file in another class

I want to delete the file which is opened and done writing but not closed. Please refer to code below:
Class A (can't be changed):
import java.io.FileOutputStream;
public class A {
public void run(String file) throws Exception {
FileOutputStream s = new FileOutputStream(file);
}
}
Class B:
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class B {
public static void main(String[] args) throws Exception {
String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";
A a = new A();
a.run(path);
File f = new File(path);
Files.delete(Paths.get(f.getAbsolutePath()));
}
}
In Class A , just open the stream without closing the file.
In class B , calling A's run method and then try to delete the file.
Since the file is still opened. I'm unable to delete the file.
Error is :
The process cannot access the file because it is being used by another process.
Actual Scenario is :
We are loading the jars dynamically. Classes inside jar are creating the file. When there is an exception, a file gets created whose size will be 0 bytes. We need to delete this file. Since the file is not closed during the exception, we can't delete the file.
We could fix the issue if we could close the streams in the jar classes, but we can't modify the jars that create the files as they are client specific jars.
Please suggest how to delete the opened file, without modifying the code in class A.
Make sure you close the file, even if there was an Exception when writing to it.
E.g.
public void run(String file) throws Exception {
FileOutputStream s = null;
try {
s = new FileOutputStream(file);
} finally {
try {
s.close();
} catch(Exception e) {
// log this exception
}
}
}
You have to close the file before any delete operation as firstly its a bad practice and second is it will lead to memory leaks.
If you are using Tomcat, it is possible to set AntiLockingOption and antiJARLocking in $CATALINA_HOME/conf/context.xml for Windows:
<Context antiJARLocking="true" antiResourceLocking="true" >
Important note:
The antiResourceLocking option can stop JSPs from redeploying when they are edited requiring a redeploy.
Read more about this option:
http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
antiResourceLocking:
If true, Tomcat will prevent any file locking. This will significantly impact startup time of applications, but allows full webapp hot deploy and undeploy on platforms or configurations where file locking can occur. If not specified, the default value is false.
Pass the resource as a parameter and it becomes the caller's responsibility to clear up the resources
public void run(FileOutputStream stream) throws Exception {
...
}
caller:
try(FileStream stream = new FileStream(path)){
A a = new A();
a.run(stream);
}catch(Exception e){
.. exception handling
}
Updated according to OPs comment.
Another approach could be to subclass A and override run().
public static void main(String[] args) throws Exception {
String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";
A a = new A() {
#Override
public void run(String file) throws Exception {
FileOutputStream s = new FileOutputStream(file);
s.close();
}
};
a.run(path);
File f = new File(path);
Files.delete(Paths.get(f.getAbsolutePath()));
System.out.println("foo");
}
I don't think you'll find a pure java solution to this problem. One option is to install Unlocker (being careful to hit "Skip" on all the junkware) and invoke it from your code.
If you have UAC enabled, you'll also need to be running your java in an elevated process (e.g. start command prompt as Administrator). Then, assuming unlocker is in C:\Program Files\Unlocker:
Process p = new ProcessBuilder("c:\\Program Files\\Unlocker\\Unlocker.exe",path,"-s").start();
p.waitFor();
And after that you can delete the file as before. Or you could use "-d" instead of "-s" and Unlocker will delete the file for you.

Get java.lang.Class object of a class by reading its source(.java) file

I want to get the java.lang.Class object of a class by reading its source file using FileReader.
Actually I want to get all methods, constructors, parent class, overridden methods and imported packages of the class by selecting its source file using JFileChooser. So, I think I got these all things by using its class Class object methods like getConstructors() etc.
I have tried this, but it gives java.lang.ClassNotFoundException...
public static void main(String[] args) {
File file = new File(
"F:\\study\\projects\\saralbhakti\\src\\signup\\SignupServlet.java");
try {
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[] { url };
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("signup.SignupServlet");
System.out.println("Class Name : " + cls.getName());
Method[] declaredMethods = cls.getDeclaredMethods();
System.out.println("All Methods : " + declaredMethods.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Classes are loaded from .class files, not .java files. You have two options:
1) Use a different API, like AST parsing, which is designed to read and understand .java files (but not execute the code in them)
2) Programmatically compile the .java file, then read the .class file. This is ugly and wonky and horrible and full of caveats and probably not what you want to do.

Loading classes dynamically from jar

I know that we can load classes dynamically by using custom class loaders.
But here my problem is my Class itself is depends upon other classes
My task is to get PigServer object .So I have used following code to load PigServer class
_pigServerClass = _classLoader.loadClass("org.apache.pig.PigServer");
But here PigServer class itself is depends upon so many other classes.
So when i am trying to get instance of PigServer class then it is showing following errors
java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
java.lang.ClassNotFoundException:org.apache.log4j.AppenderSkeleton
etc..
Can anyone tell how to solve this?
There seems to be a misunderstanding. If you have all the jars required in a folder, say "lib", you can for example set up a class loader like this:
File libs = new File("lib");
File[] jars = libs.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().toLowerCase().endsWith(".jar");
}
});
URL[] urls = new URL[jars.length];
for (int i=0; i<jars.length; i++) {
urls[i] = jars[i].toURI().toURL();
}
ClassLoader uc = new URLClassLoader(urls,this.getClass().getClassLoader());
Class<?> pigServerClz = Class.forName("org.apache.pig.PigServer", false, uc);
Object pigServer = pigServerClz.newInstance();
// etc...
How you created your ClassLoader?
Did you specified another "parent" classloader, on wich classloading can be delegated?

Method to dynamically load java class files

What would be a good way to dynamically load java class files so that a program compiled into a jar can read all the class files in a directory and use them, and how can one write the files so that they have the necessary package name in relation to the jar?
I believe it's a ClassLoader you're after.
I suggest you start by looking at the example below which loads class files that are not on the class path.
// Create a File object on the root of the directory containing the class file
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURI().toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
Class myclass = ClassLoader.getSystemClassLoader().loadClass("package.MyClass");
or
Class myclass = Class.forName("package.MyClass");
or loading the class from different folder which is not in the classpath:
File f = new File("C:/dir");
URL[] cp = {f.toURI().toURL()};
URLClassLoader urlcl = new URLClassLoader(cp);
Class myclass = urlcl.loadClass("package.MyClass");
For further usage of the loaded Class you can use Reflection if the loaded Class is not in your classpath and you can not import and cast it. For example if you want to call a static method with the name "main":
Method m = myclass.getMethod("main", String[].class);
String[] args = new String[0];
m.invoke(null, args); // invoke the method
If you add a directory to your class path, you can add classes after the application starts and those classes can be loaded as soon as they have been written to the directory.

Categories

Resources