1

I have a static block in my class, so that when a classloader loads the class definition - a method is called from this static block. The purpose of the method is to load configuration from one of the available paths. I want the first available path to be used.

There are 2 configuration files (metrics_config.json, metrics_config_new.json) and each of them could be in one of the 2 paths:

private static final File CONFIG_FILE_1 = new File("file:///dest1/config/metrics_config.json");
private static final File CONFIG_FILE_2 = new File("file:///dest2/config/metrics_config.json");

private static final File CONFIG_FILE_3 = new File("file:///dest1/config/metrics_config_new.json");
private static final File CONFIG_FILE_4 = new File("file:///dest2/config/metrics_config_new.json");

How do I pass only one path that is currently available to the method? I suppose this may look similar to this:

static {
    // choose 1st file's path
    if (CONFIG_FILE_1.exists() && CONFIG_FILE_1.isFile()) {
        loadConfiguration(CONFIG_FILE_1);
    }
    if (CONFIG_FILE_2.exists() && CONFIG_FILE_2.isFile()) {
         loadConfiguration(CONFIG_FILE_2);
    }
    // choose 2nd file's path
    if (CONFIG_FILE_3.exists() && CONFIG_FILE_3.isFile()) {
         loadConfiguration(CONFIG_FILE_3);
    }
    if (CONFIG_FILE_4.exists() && CONFIG_FILE_4.isFile()) {
         loadConfiguration(CONFIG_FILE_4);
    }
}

The downside of my current solution is that it will check both paths for the first file and for the second file. What would be the correct way to determine which path to pass to the loadConfiguration method?

Bas de Groot
  • 676
  • 3
  • 16
samba
  • 2,821
  • 6
  • 30
  • 85

2 Answers2

2

The solution to this is really not to do it. Don't have static code blocks, or other class initialization code, do operations that have a high chance of failure because of environment problems such as IO (other than using the classloader).

Also

  • Ensuring good error handling when doing so is just too difficult. Checked exceptions, include IOException, may not propagate out of them, which means all the error handling in you IO code must be self contained.
  • It makes unit testing using Dependency Injection very difficult.
  • The order that static blocks in different classes are executed is not obvious and can change if you refactor your code. This makes dependencies between those static blocks difficult to get right if your code is complicated. Doing IO properly is complicated.
  • The static blocks are executed while a lock is held. This can cause deadlock if your static block directly or indirectly tries to do any multithreading. For example, by using streams. The only way to avoid such problems is to keep the code in the block very simple. As I said before, IO code is complicated. It inevitably involves delegation to other classes in a complicated and non obvious way (encapsulation and information hiding is a feature, not a bug). Can you be sure none of the encapsulated code will lead to deadlock?
  • And importantly, static object reference won't be garbage collected until the respective class loader is unloaded. So when the reference is to an external resource, then the impact due to a possible memory leak could be severe.

Instead move configuration file reading into your main method, or a method called from it.

Raj
  • 778
  • 1
  • 10
  • 14
Raedwald
  • 46,613
  • 43
  • 151
  • 237
1

You could use else if to do something like this:

static {
    // choose 1st file's path
    if (CONFIG_FILE_1.exists() && CONFIG_FILE_1.isFile()) {
        loadConfiguration(CONFIG_FILE_1);
    }
    else if (CONFIG_FILE_2.exists() && CONFIG_FILE_2.isFile()) {
         loadConfiguration(CONFIG_FILE_2);
    }
    // choose 2nd file's path
    if (CONFIG_FILE_3.exists() && CONFIG_FILE_3.isFile()) {
         loadConfiguration(CONFIG_FILE_3);
    }
    else if (CONFIG_FILE_4.exists() && CONFIG_FILE_4.isFile()) {
         loadConfiguration(CONFIG_FILE_4);
    }
}

In the best case this would mean that only 2 of the paths have to be checked, however in the worst case all paths still need to be checked.

To avoid code duplication it might also be a nice idea to create a separate method to fetch the correct files.

public static File fetchConfigFile(file1, file2) {
    if (file1.exists() && file1.isFile()) {
        return file1;
    }
    else if (file2.exists() && file2.isFile()) {
         return file2;
    }
    // Be aware: returning null might not be what you want. 
    return null;
}

And than the static block can be simplified to this:

static {
    loadConfiguration(fetchConfigFile(CONFIG_FILE_1, CONFIG_FILE_2));
    loadConfiguration(fetchConfigFile(CONFIG_FILE_3, CONFIG_FILE_4));
}
Bas de Groot
  • 676
  • 3
  • 16