4

I have a problem, I wish to use reflection to generate instances of one of a set of classes at runtime. However, I have hit a snag. I want to get all of the classes involved to register themselves so the appropriate class can be chosen from a GUI. I can do this using a static code block in each file which provides a tidy OO solution. However, the java class loader specifically loads classes when they are required, hence if a needed class has not yet been used, it is not registered.

Short of directly providing a static list of names, or running through the underlying class/java files (which would break when packaged into a jar anyway), is there any way to force classes of certain packages to be loaded?

Basically, I want the ability to add new classes, from a specified superclass, without having to change/add any other code.

Hubris
  • 1,842
  • 2
  • 18
  • 26
  • There actually seems to have been a couple of posts that cover this to an extent, but none seem to provide a less-than-extremely-complex or satisfactory method of doing it. See: http://stackoverflow.com/questions/205573/java-at-runtime-find-all-classes-in-app-that-extend-a-base-class http://stackoverflow.com/questions/176527/how-can-i-enumerate-all-classes-in-a-package-and-add-them-to-a-list#189525 – Hubris Aug 13 '09 at 23:07
  • or http://stackoverflow.com/questions/205573/java-at-runtime-find-all-classes-in-app-that-extend-a-base-class – Hubris Aug 13 '09 at 23:09
  • I'd modify the title of your question, since you're really asking about enumerating available classes, not dynamically loading them. – Daniel Pryden Aug 13 '09 at 23:29

7 Answers7

3

To clarify, your problem isn't about "dynamic class loading in Java", it's about dynamic class enumeration -- you know how to load classes, you just don't know what classes you want.

A quick Google came up with this page: http://forums.sun.com/thread.jspa?threadID=341935&start=0&tstart=0

Taken from that page, here's some sample code that ought to work:

public static Class[] getClasses(String pckgname)
        throws ClassNotFoundException {
    ArrayList<Class> classes = new ArrayList<Class>();
    // Get a File object for the package
    File directory = null;
    try {
        ClassLoader cld = Thread.currentThread().getContextClassLoader();
        if (cld == null) {
            throw new ClassNotFoundException("Can't get class loader.");
        }
        String path = '/' + pckgname.replace('.', '/');
        URL resource = cld.getResource(path);
        if (resource == null) {
            throw new ClassNotFoundException("No resource for " + path);
        }
        directory = new File(resource.getFile());
    } catch (NullPointerException x) {
        throw new ClassNotFoundException(pckgname + " (" + directory
                + ") does not appear to be a valid package");
    }
    if (directory.exists()) {
        // Get the list of the files contained in the package
        String[] files = directory.list();
        for (int i = 0; i < files.length; i++) {
            // we are only interested in .class files
            if (files[i].endsWith(".class")) {
                // removes the .class extension
                classes.add(Class.forName(pckgname + '.'
                        + files[i].substring(0, files[i].length() - 6)));
            }
        }
    } else {
        throw new ClassNotFoundException(pckgname
                + " does not appear to be a valid package");
    }
    Class[] classesA = new Class[classes.size()];
    classes.toArray(classesA);
    return classesA;
}
Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • 1
    @Hubris: Ohh noooo.. that's a) running through the file system and it's ugly and breaks DRY... : http://stackoverflow.com/questions/1275113/dynamic-class-loading-in-java-enumeration/1275224#1275224 – OscarRyz Aug 13 '09 at 23:46
  • 1
    @Daniel: Just in case you didn't notice in your finding you ARE loading the classes anyway with: classes.add( Class.forName( ..... – OscarRyz Aug 13 '09 at 23:48
2

Spring Framework does component scanning based on annotations.

Take a look at ClassPathScanningCandidateComponentProvider class, for example. You can do the same thing based on interface / base class and it should work for all LOCAL classes. There is no way to do this for ALL classes in java.

ChssPly76
  • 99,456
  • 24
  • 206
  • 195
1

Class.forName("class name here");

You'll have a String variable with the class name. Assuming you have a List of Strings containing all class names you want to be loaded (and automatically registered using static code blocks:

for(String className : classesToBeLoaded) Class.forName(className);

Marian
  • 2,617
  • 1
  • 16
  • 13
  • I think you misunderstand... I don't want to provide a static name. – Hubris Aug 13 '09 at 23:05
  • It is not static. I showed you how (after edit). If you want to list every class in a package, it's impossible if you don't iterate through the file system. By design, the class loaders in Java cannot list classes (think about a HTTP-based class loader), but if you know you'll use the default classloader, you can browse all the folders and all the jars (which are zip files) for the classes belonging to your packages. Anyway, providing the list of classes as configuration/input for your program is safer and easier to implement. – Marian Aug 13 '09 at 23:13
1

Go through this example:

public class MainClass {

  public static void main(String[] args){

    ClassLoader classLoader = MainClass.class.getClassLoader();

    try {
        Class aClass = classLoader.loadClass("com.jenkov.MyClass");
        System.out.println("aClass.getName() = " + aClass.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}
Kevin Boyd
  • 12,121
  • 28
  • 86
  • 128
1

There is a chance if your classes are in jars in the filesystem ( this may sound ridiculous but I've seen classes loaded from ldaps )

So, again if your classes are in the filesystem inside jars you can do the following:

Pseudo-java below:

  String classPath = System.getClassPath(); 
  String [] classPathParts = classpath.split(";"); // or :

  String [] allTheClasses = []

  for each ( file  in classPathParts ) {
      if( file is directory ) {
           allTheClasses.addAll( file.contents );
      } else if ( file is ".jar" ) {
           allTheClasses.addAll( getZippedNamesFrom( file ) );

      }
 }

 // At this point you would have all the classes in the array ( list ) .

 String [] packageNames = {"a.b.c", "d.e.f" };
 for each ( String  clazzName in allTheClasses ) {
       if ( packageNames.contains( clazzName.getPackageName() ) ) {
           Class.forName( clazzName ); // load it and have the static block run
           // Or run it your self
       }
 }

 // End of the story.

I did this for a similar thing a long while ago.

I use to load about 70k+ classes names using this approach in less than a second. In my case I was dynamically looking for about 10 classes so the whole process took me about 1.1 second.

OscarRyz
  • 196,001
  • 113
  • 385
  • 569
1

You can use ClassFinder,

http://jmeter.apache.org/api/org/apache/jorphan/reflect/ClassFinder.html

UBIK LOAD PACK
  • 33,980
  • 5
  • 71
  • 116
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
0

I would suggest putting all the "dynamic" classes into one package. Scan that directory to pull out each filename and then use that in Class.forName() to load and register each class.

I just asked a similar question yesterday, and got some good feedback.

Create new class from a Variable in Java

Community
  • 1
  • 1
jW.
  • 9,280
  • 12
  • 46
  • 50