How can I enumerate all child classes of a superclass in Java - java

My application consists of several JAR files. I would like to iterate over all of the JAR files and find all classes that subclass a particular class, so that I can cause their static initializers to run.
I looked though the Javadocs for java.lang.ClassLoader, but can't find any method that does this.
I am trying to implement the "Product Trader" pattern (http://www.ubilab.com/publications/print_versions/pdf/plop-96-producttrader.pdf), with the classes "self-registering" in the abstract superclass. The superclass will have a HashMap that maps the service a subclass provides and the java.lang.Class file that handles that service.
The problem is that a class does not run it's static initializers until it is loaded. If I can iterate all of the subclasses, I can force each to load and run it's initializers.
Thanks, Ralph

This is not generally solvable. A ClassLoader is not required to be able to iterate over the Classes it could load. The easiest example of a ClassLoader that can't do that is when you load classes from a http:// base URL: HTTP provides no standardized way to enumerate the content of any given URL.
An better way to implement this is to use the ServiceLoader and let all implementing classes register themselves via a simple entry in their jar file.

This is obviously not solvable very simply.
However, there are solutions, like the one mentionned in this Javaworld article.
Generally speaking, it consists into exploration all elements from the CLASSPATH. if they're jar files, explore them looking for classes, load those classes and see if they extend you reference base class.
However, i would strongly suggest you use a more "mature" mechanism like IoC or a plugin architecture.
I'm obviously thinking about JSPF, as an example.

If you really need to do this the only real way is to iterate through the classpath, scanning the jars and directories for class files, loading that class and looking at it's parent class.
Note, some classes will have static initialising code that can have bad side affects, e.g. loading X11 classes from the runtime can hang for a long long time. If possible try to restrict what classes you load to specific packages, e.g. com.company (you can of course identify the package by the path of the class file relative to the root to the classpath item).
[Note the ServiceLoader suggested by Joachim Sauer, or a framework that provides a similar mechanism is a much better solution]

Related

How to scan for a particular annotation of java classes loaded at runtime as a bytecode?

If a java class loaded at runtime as a bytecode (e.g. via ASM library or other mechanism), is it on a classpath of java? I don't see it.
How to scan all annotations of classes loaded this way, if it's not on the java classpath?
I use google reflections and a custom classloader of a 3-rd party library.
Scanning for classes works as follows:
Reflections reflections = new Reflections(ClasspathHelper.forPackage("com.mypackage",
MyCustomClassLoader),
new SubTypesScanner(), new TypeAnnotationsScanner());
Set<Class<?>> myClasses = reflections.getTypesAnnotatedWith(MyAnnotation.class);
MyAnnotation - is marked as #Retention(RetentionPolicy.RUNTIME).
The above (in a class) is loaded dynamically at runtime by JVM.
As can be seen, behind the scenes, Reflections tries to get all URLs to be scanned using 2 classloaders by default static and context.
Reflection.scan()
Update: I have found an answer Can you find all classes in a package using reflection? saying that "If there are classes that get generated, or delivered remotely, you will not be able to discover those classes." But there is no proof, however.
Can please anybody give more details on this and confirm?
Dynamically instantiating classes at run time does not change the classpath that your JVM is using. What happens is that some ClassLoader class fetches bytecode from somewhere; and makes it "available" to you. But that does in no way change the "search order" for loading classes (and that is basically what the classpath is about: it only tells the JVM where and in which order to look for classes to load).
Meaning: any "loading" of a class results in some object of class java.lang.Class.
If you want to query the structure of any loaded class; you "just" need to get to the corresponding Class object. Class provides methods like getAnnotation(). It also provides methods to retrieve other objects that represent the methods and fields of the specific Class; and those objects can be queried for annotations in similar ways.
Update, regarding the updates in the question: there are situations where you don't have access to the file system where classes are coming from. You can load classes, when you know their name, but you have no way looking into the "place" where these classes live. And that basically breaks your ability to use reflection as intended.
There is no need to "prove" that, it is simply a consequence of the fact that Java allows you to load classes when you know there name, but "hides" the exact content of "where" those classes are coming from.

Is there a way to unpack jars in runtime and access methods of classes?

This might be a little vague but I wanted to know if I have a class name (including the package) before hand, say in a file and I have a bunch of jars in the classpaths. Then is there a way I can look into a particular jar to find that class and access some method from that class?
What I am actually trying to do is to load some values from different class, dynamically. If the above approach is flawed then is there any other way to do it? Am not completely certain about how the class loader works, so I didn't want to go down that rabbit hole just yet.
EDIT An abstract example of what am trying to do:
I have a program that builds a graph for me using certain information, for the purpose of this example lets say that information is alphabets. I have some jars in my classpath that have a class that has a method that returns these alphabets for that specific class. Now I got the order of the classes I need to build the graph from, so if I am able to extract these alphabets from the jars, I can build my graph.
You can use a URLClassLoader and its methods to find classes in a .jar file (typically) once you have the Class object, you can use java.lang.reflect to instantiate objects, call methods, or inspect variables
If the jars are your own: Consider using java.util.ServiceLoader. It helps to find any classes residing on the classpath at runtime that implement a given Interface, as long as you declare them in a META-INF/services list.
So you let the classloader load the classes without knowing their names and simply call their methods.
I'm not sure if there is a clean way to find out (enumerate) the names of all classes only by reflection without scanning the jars as a zip.

if I have a classloader instance, can I find where it looks for the class bytes?

if I have dira,jarb and dirc in the classpath in that order, and I have a java app with 3 classloaders with a parent/child/grandchild relationship, will they all read the same directory ?
I guess I am trying to figure out where each classloader looks... is there a way to find this path given an instance of the classloader ?
In general no, a classloader is permitted to construct bytes however it likes. E.g. the JSP classloader might invoke the JSP compiler dynamically if the JSP file has a recent timestamp.
Running the JVM with the -verbose:class flag will enable a lot of logging which should help you if you're just using the standard bootstrap classloaders.
If there's some custom classloader, you could supply your own URLConnectionFactory and see what URLs are being fetched.
You have actually several questions here.
The classes in the classpath directories and jars will usually be loaded by one classloader (the application classloader), not by several ones for each entry.
If you have classloaders in a parent-child-relationship, the child one should first ask its parent to load the class and only lookup the bytecode itself when the parent did not find anything. (There are special-purpuse classloaders in some frameworks which do this the other way around. If each class exists only once, then this should not make a difference.)
If you have an URLClassLoader, then you can ask its getURLs() method to find out from where it loads. For other classloaders, there may or may not be a way to find this.
Take a look at the ClassLoader API and you will realise there is a method that passes a name and eventually the class loader passes a byte[] to define the class. Because it is a proper class it can grab those bytes from anywhere it wants to. ClassLoader is just another public class anyone can implement their own implementation and do their own thing. ClassLoaders are everywhere, we have the version that reads the classpath system property, in tomcat we have another that reads from a war file, in osgi it reads from a jar file. Each does a few extra things besides simply reading some file and tahts the beauty and flexibility of classloading.
There is no method on ClassLoader that returns a String, because what would it return given the above mentioned CLassLoaders ? A file path, a jar file path ? etc
In general no, but in practice you often want to find out where some class, resource is being loaded from and you can do,
System.out.println(someClassLoader.getResource("someResource.txt"));
Even more useful, if you are looking to find which .class file a Class is from, do
Class c = SomeClass.class;
System.out.println(c.getResource(c.getSimpleName() + ".class"));
The above is not guaranteed to work if the .class file is generated dynamically, but works in most situations.

hide some class file or package in jar file

How can I export a jar without some class or package which I don't want others to use,but which is needed in project,means I can't delete them.
This doesn't make a lot of sense.
If the classes are needed to compile the project, then there are likely to be static dependencies on them in other classes. (Otherwise ... you would be able to just delete it / them.)
But if there are static dependencies on those classes, then you won't be able to run the applications unless those classes are present in the JAR file. If you leave the classes out of the JAR to stop people using them (or whatever), your application will die on startup with "class not found" exceptions.
If you want to stop people using the classes directly, you could try the following:
Change the classes access to "package private". This doesn't make it impossible to use them, but it makes it more difficult.
Change your project so that the dependencies on the classes are entirely dynamic; e.g. via Class.forName(...) or dependency injection. Then you can exclude the classes from the JAR as required. But once again, if your application needs to use the classes, they have to be accessible at runtime (somehow), and it will therefore be possible (somehow) for other people to get hold of them.
Change your project to allow you remove the classes entirely. This is the only sure-fire solution.
Finally, before you go to all of this trouble you should ask yourself why you are even bothering to do this. Why don't you just let people use the classes anyway? What is to stop them getting the classes from somewhere else ... or implementing their own versions from scratch?
If the class is needed for your program to work, you can't omit it from the JAR.
You can put classes in a package that has something like "internal" in its name — e.g. com.example.internal — to indicate that the classes aren't meant for others to use or rely on. It can't prevent anyone from using your class for their own purposes, but it at least indicates that they do so at their own risk, that your internal interfaces might change in future versions of the program, etc.
A simple way is to use the package private access modifier for classes. That will make it difficult for others to access the classes, but it will also affect your ability to use them from other packages.
A more advanced way would be to use OSGi and only publish those classes you want others to use. That will make it impossible for others to access the classes while not restricting your access to them.
Put them into a separate jar file, include that jar file (as a single file, maybe with a nondescript name) into your jar file, create a custom class loader to open that embedded jar file from your code, bootstrap an entry point with reflection (because you cannot have static dependencies on it) into that new class loader .
Or just accept the fact that since the code is in there somewhere, any amount of obfuscation is just an inconvenience to everyone and cannot really hide anything.

Is there a way to get all the classes that implement a certain method?

The title speaks for itself. The language is Java.
Yes, there is. This is however a tedious and expensive work. You need to crawl through all class files and all JAR files with help of ClassLoader#getResources() and a shot of java.io.File and load all classes of it with help of Class#forName() and finally check if the method is there by Class#getMethod().
However, there are 3rd party API's which can take the tedious work from hands, but it is still expensive, because loading a class would cause its static initializers being executed.
A cleaner way is to make use of annotations and annotate the methods in question and then make use of libraries which searches for classes/methods/fields based on the annotations, such as Google Reflections.
On the other hand, if the entire package name or the JAR file name is known beforehand, then the work will be less tedious and expensive (no need to do stuff recursively nor to load the all of the classes of entire classpath).
Update: I remember, I ever wrote sample code to achieve something like that, you can find it here. It's good to start with, you only need to change it a bit to check the method.
No, you can't, in general. If you could get a complete list of available classes you could check each of them using reflection - but you can't ask a classloader for a list of everything that's available. (For instance, it may be fetching classes over HTTP, and may not know all the files available.)
If you knew that you were interested in classes in a jar file, however, you could open the jar file, find all the class files within it and ask the classloader for those classes. It would be somewhat fiddly.
What's the bigger picture here? There may be a better way to approach the problem.
Also, in Eclipse, you can simply ask for this :
Clic on the method, and type Ctrl-T.

Categories

Resources