Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to register a java class if the static initializer isn't called till the class is referenced

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?

like image 404
AgostinoX Avatar asked Aug 06 '13 21:08

AgostinoX


1 Answers

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.

like image 89
Ravi K Thapliyal Avatar answered Sep 29 '22 19:09

Ravi K Thapliyal