Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way of reading configuration parameters from configuration file in Java?

Let us assume up to runtime we do not know what are the details of configuration(may user need to configure these parameters in config file before running the application.

I want to read those configuration details and need to reuse them wherever I need them in my application. For that I want to make them as global constants(public static final).

So, My doubt is, is there any performance implications if I read from config file directly from the required class? since,runtime values I can not directly put in separate Interface.

I am thinking it will impact performance.Please suggest me any better way to do this.

UPDATE: Can I use separate final class for configuration details? putting all configuration details as constants in a separate public final class
(To read all configuration details at once from the configuration file and storing them as global constants for later use in application)

like image 399
Sagar Pudi Avatar asked Nov 13 '14 12:11

Sagar Pudi


People also ask

What is the best config file format?

Simple configuration languages, such as JSON, work for many applications, but when you need proper validation, schema, and namespace support, XML is often best.

How do I read a .conf file?

If you need to open a CONF file, you can use TextMate in macOS or GNU Emacs in Linux. Some examples of configuration files include rc. conf for the system startup, syslog. conf for system logging, smb.


8 Answers

I am thinking it will impact performance.

I doubt that this will be true.

Assuming that the application reads the configuration file just once at startup, the time taken to read the file is probably irrelevant to your application's overall performance. Indeed, the longer the application runs, the less important startup time will be.

Standard advice is to only optimize for application performance when you have concrete evidence (i.e. measurements) to say that performance is a significant issue. Then, only optimize those parts of your code that profiling tells you are really a performance bottleneck.


Can I use separate final class for configuration details

Yes it is possible to do that. Nobody is going to stop you1. However, it is a bad idea. Anything that means that you need to recompile your code to change configuration parameters is a bad idea. IMO.


To read all configuration details at once from the configuration file and storing them as global constants for later use in application.

Ah ... so you actually want to read the values of the "constants" instead of hard-wiring them.

Yes, that is possible. And it makes more sense than hard-wiring configuration parameters into the code. But it is still not a good idea (IMO).

Why? Well lets look at what the code has to look like:

public final class Config { 
    public static final int CONST_1;
    public static final String CONST_2;
   
    static {
        int c1;
        String c2;
        try (Scanner s = new Scanner(new File("config.txt"))) {
            c1 = s.nextInt();
            c2 = s.next();
        } catch (IOException ex) {
            throw RuntimeException("Cannot load config properties", ex);
        }
        CONST_1 = c1;
        CONST_2 = c2; 
    }
}

First observation is that makes no difference that the class is final. It is declaring the fields as final that makes them constant. (Declaring the class as final prevents subclassing, but that has no impact on the static fields. Static fields are not affected by inheritance.)

Next observation is that this code is fragile in a number of respects:

  • If something goes wrong in the static initializer block. the unchecked exception that is thrown by the block will get wrapped as an ExceptionInInitializerError (yes ... it is an Error!!), and the Config class will be marked as erroneous.

  • If that happens, there is no realistic hope of recovering, and it possibly even a bad idea to try and diagnose the Error.

  • The code above gets executed when the Config class is initialized, but determining when that happens can be tricky.

  • If the configuration filename is a parameter, then you have the problem of getting hold of the parameter value ... before the static initialization is triggered.

Next, the code is pretty messy compared with loading the state into a instance variables. And that messiness is largely a result of having to work within the constraints of static initializers. Here's what the code looks like if you use final instance variables instead.

public final class Config { 
    public final int CONST_1;
    public final String CONST_2;
   
    public Config(File file) throws IOException {
        try (Scanner s = new Scanner(file)) {
            CONST_1 = s.nextInt();
            CONST_2 = s.next();
        } 
    }
}

Finally, the performance benefits of static final fields over final fields are tiny:

  • probably one or two machine instructions each time you access one of the constants,

  • possibly nothing at all if the JIT compiler is smart, and you handle the singleton Config reference appropriately.

In either case, in the vast majority of cases the benefits will be insignificant.


1 - OK ... if your code is code-reviewed, then someone will probably stop you.

like image 55
Stephen C Avatar answered Sep 25 '22 07:09

Stephen C


Have you ever heard of apache commons configuration http://commons.apache.org/proper/commons-configuration/ ? It is the best configuration reader I have ever found and even am using it in my application which is running in production since 1 year. Never found any issues, very easy to understand and use, great performance. I know its a bit of dependency to your application but trust me you will like it.

All you need to do is

Configuration config = new ConfigSelector().getPropertiesConfiguration(configFilePath);
String value = config.getString("key");
int value1 = config.getInt("key1");
String[] value2 = config.getStringArray("key2");
List<Object> value3 = config.getList("key3");

And thats it. Your config object will hold all the config values and you can just pass that object to as many classes as you want. With so many available helpful methods you can extract whichever type of key you want.

like image 39
Sri777 Avatar answered Sep 25 '22 07:09

Sri777


It will be only one time cost if you are putting them in a property file and reading the file at the start of your application and initialize all the parameters as system parameters(System.setProperty) and then define constants in your code like

public static final String MY_CONST = System.getProperty("my.const");

But ensure the initialization at start of your application before any other class is loaded.

like image 32
Sanjeev Kumar Avatar answered Sep 24 '22 07:09

Sanjeev Kumar


There are different types of configuration.

Usually some sort of bootstrapping configuration, for example to connect to a database or service, is needed to be able to start the application. The J2EE way to specify database connection parameters is via a 'datasource' specified in your container's JNDI registry (Glassfish, JBoss, Websphere, ...). This datasource is then looked up by name in your persistence.xml. In non-J2EE applications it is more common to specify these in a Spring context or even a .properties file. In any case, you usually need something to connect your application to some sort of data store.

After bootstrapping to a data store an option is to manage config values inside this datastore. For example if you have a database you can use a separate table (represented by e.g. a JPA Entity in your application) for configuration values. If you don't want/need this flexibility you can use simple .properties file for this instead. There is good support for .properties files in Java (ResourceBundle) and in frameworks like Spring. The vanilla ResourceBundle just loads the properties once, the Spring helper offers configurable caching and reloading (this helps with the performance aspect which you mentioned). Note: you can also use Properties backed by a data store instead of a file.

Often both approaches coexist in an application. Values that never change within a deployed application (like the application name) can be read from a properties file. Values that might need to be changed by an application maintainer at runtime without redeployment (e.g. the session timeout interval) might better be kept in a reloadable .properties file or in a database. Values that can be changed by users of the application should be kept in the application's data store and usually have an in-application screen to edit them.

So my advise is to separate your configuration settings into categories (e.g. bootstrap, deployment, runtime and application) and select an appropriate mechanism to manage them. This also depends on the scope of your application, i.e. is it a J2EE web app, a desktop app, command-line utility, a batch process?

like image 45
Adriaan Koster Avatar answered Sep 24 '22 07:09

Adriaan Koster


What kind of configuration file do you have in mind? If it is a properties file, this might suit you:

public class Configuration {

    // the configuration file is stored in the root of the class path as a .properties file
    private static final String CONFIGURATION_FILE = "/configuration.properties";

    private static final Properties properties;

    // use static initializer to read the configuration file when the class is loaded
    static {
        properties = new Properties();
        try (InputStream inputStream = Configuration.class.getResourceAsStream(CONFIGURATION_FILE)) {
            properties.load(inputStream);
        } catch (IOException e) {
            throw new RuntimeException("Failed to read file " + CONFIGURATION_FILE, e);
        }
    }

    public static Map<String, String> getConfiguration() {
        // ugly workaround to get String as generics
        Map temp = properties;
        Map<String, String> map = new HashMap<String, String>(temp);
        // prevent the returned configuration from being modified 
        return Collections.unmodifiableMap(map);
    }


    public static String getConfigurationValue(String key) {
        return properties.getProperty(key);
    }

    // private constructor to prevent initialization
    private Configuration() {
    }

}

You could also return the Properties object immediately from the getConfiguration() method, but then it could potentially be modified by the code that access it. The Collections.unmodifiableMap() does not make the configuration constant (since the Properties instance gets its values by the load() method after it was created), however since it is wrapped in an unmodifiable map, the configuration cannot be changed by other classes.

like image 25
matsev Avatar answered Sep 28 '22 07:09

matsev


Well this is a great problem which is faced in every one's life once in a will. Now coming to the problem, this can be solved by creating a singleton class which has instance variables same as in configuration file with default values. Secondly this class should have a method like getInstance() which reads the properties once and every times returns the same object if it exists. For reading file we can use Environmental variable to get path or something like System.getenv("Config_path");. Reading the properties (readProperties() method) should read each item from config file and set the value to the instance variables of singleton object. So now a single object contains all the configuration parameter's value and also if the parameter is empty than default value is considered.

like image 30
Bhargav Modi Avatar answered Sep 25 '22 07:09

Bhargav Modi


One more way is to define a class and read the properties file in that class. This class needs to be at the Application level and can be marked as Singleton. Marking the class as Singleton will avoid multiple instances to be created.

like image 41
Yusuf Kapasi Avatar answered Sep 28 '22 07:09

Yusuf Kapasi


I recommend using JAXB or a similar binding framework that works with text based files. Since a JAXB implementation is part of the JRE, it's pretty easy to use. As Denis I advise against configuration keys.

Here is a simple example for an easy to use and still pretty mighty way to configure you application with XML and JAXB. When you use a DI framework you can just add a similar config object to the DI context.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ApplicationConfig {

    private static final JAXBContext CONTEXT;
    public static final ApplicationConfig INSTANCE;

    // configuration properties with defaults
    private int number = 0;
    private String text = "default";
    @XmlElementWrapper
    @XmlElement(name = "text")
    private List<String> texts = new ArrayList<>(Arrays.asList("default1", "default2"));

    ApplicationConfig() {
    }

    static {
        try {
            CONTEXT = JAXBContext.newInstance(ApplicationConfig.class);
        } catch (JAXBException ex) {
            throw new IllegalStateException("JAXB context for " + ApplicationConfig.class + " unavailable.", ex);
        }
        File applicationConfigFile = new File(System.getProperty("config", new File(System.getProperty("user.dir"), "config.xml").toString()));
        if (applicationConfigFile.exists()) {
            INSTANCE = loadConfig(applicationConfigFile);
        } else {
            INSTANCE = new ApplicationConfig();
        }
    }

    public int getNumber() {
        return number;
    }

    public String getText() {
        return text;
    }

    public List<String> getTexts() {
        return Collections.unmodifiableList(texts);
    }

    public static ApplicationConfig loadConfig(File file) {
        try {
            return (ApplicationConfig) CONTEXT.createUnmarshaller().unmarshal(file);
        } catch (JAXBException ex) {
            throw new IllegalArgumentException("Could not load configuration from " + file + ".", ex);
        }
    }

    // usage
    public static void main(String[] args) {
        System.out.println(ApplicationConfig.INSTANCE.getNumber());
        System.out.println(ApplicationConfig.INSTANCE.getText());
        System.out.println(ApplicationConfig.INSTANCE.getTexts());
    }
}

The configuration file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<applicationConfig>
    <number>12</number>
    <text>Test</text>
    <texts>
        <text>Test 1</text>
        <text>Test 2</text>
    </texts>
</applicationConfig>
like image 37
chromanoid Avatar answered Sep 28 '22 07:09

chromanoid