Question

I need some advice on a paradigm to follow. Before, I had something like this

package/Instantiator.class

package/instances/GenericInstance.class (abstract)

package/instances/AInstance.class (extends Generic)

package/instances/BInstance.class (extends Generic)

What Instantiator did was to search package/instances folder for all class files, and instantiated each with reflection and call an abstract method on all the instances and collected output and save to a DB.

Howver, I have to package my code in a Jar now, and Java does not seem to allow searching for all class files in a package in a jar (since it seems to mess with pathing).

I could add all instances in a List in GenericInstance or something, and then Instantiator could just get the list of classes.

But I would like for other people to just be able to add a class to that package and that's it.

What pattern should I follow here? Any code help? Thanks!

Was it helpful?

Solution 2

My method I just discovered:

try {    

    String jarName = new File(Insantiator.class.getProtectionDomain()    
                                                            .getCodeSource()    
                                                            .getLocation()    
                                                            .getPath())    
                                                            .getName();    
    JarFile jar = new JarFile(jarName);    
    Enumeration<JarEntry> entries = jar.entries();    
    while(entries.hasMoreElements()) {    
        ZipEntry entry = entries.nextElement();    
        String name = entry.getName();    
        if(name.contains("package/instances") && !name.contains("GenericInstance.class") && name.contains(".class")) {    
            name = name.replace("/", ".").replace(".class", "");    
            try {    
                Class theClass = Class.forName(name);    
                GenericInstance instance = (GenericInstance ) theClass.newInstance();    
                instances.add(instance);    
            } catch(InstantiationException | IllegalAccessException | ClassNotFoundException e) {    
                Utilities.writeLog("---- tried: " + name);    
                Utilities.writeLogException(null, e);    
            }    
        }    
    }        

} catch (IOException e) {    
    Utilities.writeLogException(null, e);    
}

OTHER TIPS

There is one builtin solution in the JDK, which is ServiceLoader (Java 6+). However, it requires that the user create a file in META-INF/services with a list of implementations.

If your base interface is package.instances.GenericInstance then your file would be called META-INF/services/package.instances.GenericInstance and its content would be:

path.to.implementation1
path.to.implementation2

etc. Depending on the build system you use, this kind of file may be generated for you automatically (maven has a plugin, see here).

Java does not seem to allow searching for all class files in a package in a jar

Well yes it can (but the code to do it is quite complicated -- you have to create a URLClassLoader etc.).

One solution, if all jars are in your classpath at runtime, is to use something like reflections (bonus: depends on Guava, so you get all of its niceties) which has efficient filters for classes etc, and can "reflect" classloaders other than the "system" classloader; so this could work too.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top