Question

I've an interface implemented by classes that perform a file processing, say searching or whatever.

public interface FileProcessorInterface {

    public void processFile(String fileName);

}

Then i have a different implementation for each file type:

public class TxtProcessor implements FileProcessorInterface {

    @Override public void processFile(String fileName) { //do the work }

}

Thus i have the Utilizer of the processor, that has a method that allows for registering each class, something like this:

class Utilizer {

Map <String, Class> registered = new HashMap<>();

public void registerClass(String fileExt, Class clazz) {

    registered.put(fileExt, clazz);

}

public void processFile(String fileName) {

    //1) get the registered class from registered map (omitted because easy and not relevant)

    //2) create an instance of the class using reflection (omitted because easy and not relevant)
    FileProcessorInterface p = ....

    p.processFile(fileName);             

}

So far it's ok.

Now, i'm providing many implementations of my interface.

And i am tempted to provide each implementation class with a static initializer that register itself in the Utilizer, in the case of my previous TxtProcessor it would be:

class TxtProcessor implements FileProcessorInterface {

    //previous code

    static {
        Utilizer.registerClass("txt", TxtProcessor.class);
    }

}

The problem is that this static method will never be called because in the "statically reachable" code of the application there is no reference to my TxtProcessor class, since it is instantiated via reflection. So the jvm does not call the static initializer.

Say that i have two parts: the "generic code" that is the Utilizer and on the other side the implementations; it has to be thought as something provided dinamically and so it is not known by the Utilizer part.
Infact the idea was exactly that each class would register itself leaving the Utilizer untouched.
It is hard for me conceiving a solution that does not put some form of 'knowledge' of the implementations on the Utilizer side (and that stays simple), just because of the problem of the static initializer not called. How to overcome this?

Was it helpful?

Solution

Using reflections seems to be the best fit here. It's like geared to do this.

All you need is a small static block in Utilizer as

static {

    Reflections reflections = new Reflections(
                new ConfigurationBuilder()
               .setUrls(ClasspathHelper.forPackage("path.to.all.processors.pkg"))
               .setScanners(new SubTypesScanner())
           );

    reflections.getSubTypesOf(path.to.all.processors.pkg.FileProcessor.class);
}

If you don't want a third-part dependency, just add a FileProcessors.properties file to your classpath

txt=path.to.all.processors.pkg.TxtProcessor
doc=path.to.all.processors.pkg.DocProcessor
pdf=path.to.all.processors.pkg.PdfProcessor

and then register all the listed classes from Utilizer as

static {
    Properties processors = new Properties();
    try {
        processors.load(Utilizer.class
                  .getResourceAsStream("FileProcessors.properties"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    for (String ext : processors.stringPropertyNames()) {
        Utilizer.registerClass(ext, Class.forName(processors.getProperty(ext));
    }
}

This no longer requires a static block in every FileProcessor now.

OTHER TIPS

You can look at Reflections library. It allow you to find all the classes which implement an interface, have an annotation or extend a class.

You Could...

Use the same concept as JDBC does for loading it's drivers. This would require you to use Class#forName to initialize the class when the program is first loaded. While this does mean that the implementation is still dynamic from the point of view of your utility class, it is specified at run time by your application...

This gives you control over which implementation you might want to use

You Could...

Use the same concept as something like java.awt.Toolkit uses when it initializes it's instance.

It basically looks up the resource (in this case a System property) and then loads the class dynamically using Class.

Personally, I normally look for a named resource (usually a properties file) and load a key from it.

Something like getClass().getResource("/some/gloabl/configFile");, which every implementation would need to provide.

Then, if available, read the properties file and find the key I'm after.

If more then one implementation is linked in though, there is no guarantee which one will be loaded.

Quick and dirty: You can statically initialize your Utilizer in main() with correct association.
Better solution: externalize in a resource file association like

txt=path.to.package.TxProcessor

load it in Utilizer and load FileProcessorInterface implementors with Class.forName()

you can force the static init by Class.forName(fqn, true, classLoader) or the short form Class.forName(fqn)

You could have a registry file (for example, some XML file), that would contain the list of all classes you support :

<item type="txt">somepackage.TxtProcessor</item>
<item type="gif">somepackage.GIFProcessor</item>
...

Your Utilizer would load this file into its registry.

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