Java Import package into running application [duplicate] - java

I am currently loading Java classes using Class.forName() to load it.
clazz = Class.forName("interfaces.MyClass");
But now I want to load classes from different directory, I have tried to set classpath by
clazz = Class.forName("-cp \"C:/dir\" distantinterfaces.DistantClass");
With no success and ClassNotFoundException. Full path to distant class is:
C:/dir/distantinterfaces/DistantClass.class

Use an URLClassLoader for this. The code might be something along the lines of:
File f = new File("C:/dir");
URL[] cp = {f.toURI().toURL()};
URLClassLoader urlcl = new URLClassLoader(cp);
Class clazz = urlcl.loadClass("distantinterfaces.DistantClass");

Either the directory is in the classpath, and you can use Class.forName() (which only accepts fuly qualified name classes, and not -cp command line options), or it's not in the classpath and you should then use a custom class loader.
You're not saying what you really want to do (why are you loading classes dynamically), but your best bet is to have the directory in the classpath.

You have to create an instance of ClassLoader which is aware of the directory with classes. See stackoverflow questions tagged urlclassloader.

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.

Unable to access directory classes after setting classpath

I'm having a hard time setting the classpath for a directory to a package of classes. I'm trying to run a jar file that takes a directory as a command line argument. The program uses the directory to access class files in a folder and uses reflection to explore the class fields and methods.
final File folder = new File(args[0]);
classList = dirParse.listFilesForFolder(folder);
I then go through the classList, get the name of each class, and use the Class.forName() method to access the classes.
Class c = Class.forName(className);
For the line above to work, I have to set the classpath to the address of the directory containing the classes.
I can get the program to run just fine when I'm using a directory of classes that do not belong to a package like below:
java -cp "Explorer.jar:/Users/john/Desktop/TestClass/" explorer.ExplorerDemo /Users/john/Desktop/TestClass/
However, for the following line, monopoly is a package and the program throws a ClassNotFoundException after calling Class.forName(className)
java -cp "Explorer.jar:/Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/" explorer.ExplorerDemo /Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/
For testing purposes, I tried adjusting `Class.forName() call to include the package name like below:
Class c = Class.forName("monopoly."+className);
However, this also throws ClassNotFoundException.
Class.forName is a shortcut to obtaining class information within the context of ClassLoader of the current class. Javadoc states that this is equivalent to
Class.forName("Foo", true, this.getClass().getClassLoader())
Provided that you class directory is supplied as runtime parameter and is not part of the original classpath, I would suggest you instantiating custom URLClassLoader instance that will be pointing to your directory.
Sample code:
public class ReflectionClassAnalysis {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
// URLClassLoader supports both directories and jar files
Path directory = Paths.get("/some/directory/");
Path jar = Paths.get("/some/binary.jar");
// You may be interested in providing parent ClassLoader for your new instance
// You can either use current class ClassLoader like
ClassLoader contextClassLoader = ReflectionClassAnalysis.class.getClassLoader();
// or current thread ClassLoader
// ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
URLClassLoader myClassLoader = new URLClassLoader(
new URL[]{
directory.toUri().toURL(),
jar.toUri().toURL()
},
contextClassLoader
);
// You may use ClassLoader directly to load class meta
Class<?> externalClass = myClassLoader.loadClass("your.class.name");
// or supply ClassLoader to forName method
// Class.forName("your.class.name", true, myClassLoader);
// Do your class analysis here
}
}
For JAR with classpath instructions please refer to: Run a JAR file from the command line and specify classpath

Loading the classes from a jar that is dynamically uploaded through a servlet

I am uploading a jar dynamically through servlet and saving it in my WEB-INF/lib directory.
I want to get all the classes annotated with my #annotation,
have used reflections code below without any luck.. the manifest of the jar is readble but the classes are not.. the list of classes is 0
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
ConfigurationBuilder builder = new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner(),
new TypeAnnotationsScanner());
Set<URL> set = ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]));
FilterBuilder filterBuilder = new FilterBuilder().include(FilterBuilder.prefix(exportPackage));
Reflections reflections = new Reflections(builder.setUrls(set).filterInputsBy(filterBuilder));
Set<Class<? extends Object>> classSet = reflections.getTypesAnnotatedWith(MyAnnotation.class);
What changes to the configuration will help get the classes from the jar that is dynamically uploaded..
Since you are updating your own WEB-INF/lib directory it is not necessarily caught by your context class loader. BTW I think it is a bad practice: the behavior depends on the application server and this directory is probably not writable and even probably does not exist if you are running from war...
So, I'd put the jar to other directory and use my custom class loader. It is not so hard. You can use regular UrlClassLoader. Just configure it to read classes from correct path. Once this is done pass this class loader when you are creating instance of Reflections. Take a look on its javadcoc. The constructor can except various types of parameters including class loader.
from your listener class (or from wherever servletContext is available), try using:
new Reflections(ClasspathHelper.forWebInfClasses(servletContext))
or
new Reflections(ClasspathHelper.forWebInfLib(servletContext))

Adding jar file to classpath at runtime without reflection tricks

There are several posts about how to add jar-file to classpath at runtime by following idea:
- get current system classpath;
- suppose it is URLClassLoader;
- use reflection to set access for protected addURL method;
- use mentioned method to add url to classpath.
Here is an example:
Adding files to java classpath at runtime
Because of 2 and 3 steps this looks like "nasty hack".
How could I extend URLClassLoader and set it as a current? I am bit confused with classloaders and tried the following:
public static void main(String... args) {
URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:jxl.jar")});
System.out.println(loader.loadClass("jxl.Workbook"));
Thread.currentThread().setContextClassLoader(loader);
System.out.println(Class.forName("jxl.Workbook"));
} // main
I get ClassNotFoundException on the fourth line, while second works ok. (why it is so, by the way?)
The Class.forName method uses the "defining class loader of the current class," not the thread context classloader. In your case the ClassLoader that Class.forName will use is the one that loaded your application, i.e. the system class loader. This is a class loader that looks for resources in the class path.

JAR plugins implementation

Let us have a Groovy/Java application that should use a set of classes, defined in external *.jar-files (suppose they are located near the main executable jar).
So, the main class (let us call it Main) should load plugin.jar file at runtime and call some instance method on the class, defined in that jar (for some convention, suppose the class has the name as its jar - Plugin in our case).
The Main class could not know which plugins it has until it is runned. Let's throw away the CLASSPATH and java -jar run arguments and just do the magic with code only.
So, how this could be done and how the plugin.jar should be created (using Eclipse in my case) in order to be correctly loaded?
PS: yeah, i do compile my groovy sources into jar file. But i need to perform class loading and invoke exactly on-the-fly.
The secret was really simple!
Using URLClassLoader does the trick.
So, Groovy code:
ClassLoader loader = new URLClassLoader((URL[]) [
new File("C:\\Users\\errorist\\workspace\\javatest1\\bin\\").toURI().toURL()
])
Class c = loader.loadClass("src.SomeClass1")
c.invokeMethod("main", (String[]) ["Hello", "World"])
And the Java one:
File file = new File("C:\\Users\\errorist\\workspace\\javatest1\\bin\\");
URL[] urls = new URL[] { file.toURI().toURL() };
ClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("src.SomeClass1");
c.invokeMethod("main", new String[] { "Hello", "World!" });
The OSGi framework supports dynamic loading of plug-ins. There are multiple implementations, including Equinox, which underpins Eclipse itself.

Categories