views:

823

answers:

8

Hi,

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.

A: 

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
I think you misunderstand... I don't want to provide a static name.
Hubris
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
+1  A: 

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
Yes, this works, but in order to do it, you have to have a static list of class names...
Hubris
A: 

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.

http://stackoverflow.com/questions/1268817/create-new-class-from-a-variable-in-java

jW
+1  A: 

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
A: 

Thanks for all the prompt answers. Does anyone have any idea if there is a way to do this without a) running through the file system, or b) forcing each class to be loaded using ClassName.class or c) providing a static list of strings?

Sorry to be so unaccepting, but a is ugly and b&c essentially break DRY...

Hubris
What's wrong with a) Walking the filesystem and b) Invoking class.forname? That's exactly what the class loader does a) Walk the filesystem and b ) Invoke class.forname , but it does it internally obviously, it doesn't loadd all the classes ( nor should you ) but only those requested. Those two doesn't break DRY...
OscarRyz
A: 

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
+1  A: 

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
@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
@Daniel: Just in case you didn't notice in your finding you ARE loading the classes anyway with: classes.add( Class.forName( .....
OscarRyz