Property Utility and Resource Bundle Utility

Contents

1. Overview

Java has provided Properties and ResourceBundle class to deal with properties like for reading key value, retrieving all keys of properties and so on. Generally in projects we just retrieves values from properties and sometimes we retrieves formatted strings from properties and nothing else is required, Hence we creates utility for the basic requirement to deal with properties. Here we will see where to use java Properties class and where to use java ResourceBundle class and also how to create utility for both classes for essential benefits.

2. Use of Properties in projects

Generally in projects it is required to load data from properties either for configurations or for displaying messages. It is required to keep configurable data in properties so that it can be easily changed for an example database user id and password, host name, port number, connection URL and so on. Also messages like positive acknowledgement, negative acknowledgement or exceptions messages should be in properties file so that these messages can be modified for displaying.

3. Properties VS ResourceBundle

In Project we creates two separate properties, one for keeping all the configurable configurations like SMTP_HOST_NAME, DATABASE_USER, DATABASE_PASSWORD and so on and one for all the messages required to mail or SMS or for displaying to user like REGISTRATION_SUCCESS, INVALID_EMAIL_ID, INVALID_MOBILE_NUMBER and so on.

3.1. Properties

Java Properties class should be used to retrieve configurations from properties that is: all the values which is required on the project for further processing should be retrieved by Properties class for an example SMTP_HOST_NAME is required to send mail and it is configurable parameter so it must be in properties so that later we can easily change our host name without modifying our source code, now we can notice that this parameter is not a message for user so it must be retrieve by Properties class of java.

3.2. ResourceBundle

Java ResourceBundle class is used to deal with internalization messages that is: all the messages which is intentioned for users perspective, should be in properties file and also this file should be languages specific so that by Java ResourceBundle class this property values can be retrieved by choosing local.

4. Benefits of Utilities

Utilities reduces duplication of codes and also helpful for code maintenances. Below are the benefits by our utilities:

  1. Reduces duplication of codes.
  2. Just By changing in utilities will effect in all the classes who is using these utilities, hence just one place changes are required rather than all places.
  3. Properties will be loaded by class path hence if test class is using these utilities then first src\test\resources will be looked up for properties and if it is not present here then properties will be taken by src\main\resources class path. So it is really good to maintain two same properties files, one for test purpose with dummy values and another for deployment with actual values.
  4. Utilities are created in such a way that properties can be loaded from any location event if this location is not a part of class path as well.
  5. Utilities are developed in such a way that it won’t be loaded again and again for retrieving values from properties, Howsoever in our classes mentioned in Common Programming Mistakes are created with a mistake of loading properties for retrieving each key. Proper Implementation will have proper implementations of utilities.

5. Utility Flow

Below Class diagram illustrate the flow of calls.

Figure 1: Utility Flow Class Model.

  1. Caller function either retrieve Property name from MessagesConstants class to create instance of PropertyUtil or it retrieves bundle name from MessagesConstants class to create instance of BundleUtil.
  2. In case of PropertyUtil caller create instance by calling the constructor and by passing property name (taken from constants) or if property file is not in class path then PropertyUtil instance can be created by passing directory path and property name.

    In case to deal with BundleUtil caller create instance by calling the constructor and by passing bundle name (taken from constants) and if it has to be deal with language specific then caller can call constructor by passing bundle name with local.

  3. Caller takes the property key from MessagesConstants class.
  4. Caller calls getValue function of PropertyUtil by passing that key to retrieve value corresponding to that key. In case of BundleUtil caller calls getStringMessage function by passing that key to retrieve String value or caller can also call getFormatedMessage function by passing key and Objects to retrieve formatted string message.

6. Common Programming Mistakes

Here we are keeping all methods as static method and restricting the object creation hence for each call we have to pass property name and key to retrieve property value. So if we have to retrieve 10 values from same property then we have to call function 10 times with same property name with different keys and hence same property will be loaded 10 times, which is a performance affect.

6.1. PropertyUtil Java Source Code

/* Copyright (C) 2014, 2015 Sanjay Madnani
 *
 * This file is free to use: you can redistribute it and/or modify it under the terms of the GPL General Public License
 * V2 as published by the Free Software Foundation, subject to the following conditions:
 *
 * The above copyright notice should never be changed and should always included wherever this file is used.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
 * See the GNU General Public License V2 for more details. */
package com.sanjay.util;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.Properties;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Utility for Loading & Retrieving Properties values.
 *
 * @author SANJAY
 * @see Properties
 * @see MessageFormat
 */
public final class PropertyUtil {

    private static final Logger logger = LogManager.getLogger(PropertyUtil.class);

    /**
     * Private Construct to restrict object creation.
     */
    private PropertyUtil() {
    }

    /**
     * Loads a property by propertyName.
     *
     * @param path String directory of property.
     * @param propertyName String property name.
     * @return Properties loaded using argument.
     */
    private static Properties loadProperty(String path, String propertyName) {
        logger.debug("Invoking loadProperty with propertyName: " + propertyName + "...");
        try {
            Properties properties = new Properties();
            if (path == null) {
                InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(propertyName);
                properties.load(is);
                logger.debug("Property Loaded successfully");
            } else {
                logger.debug("Property Path: " + path);
                Reader reader = new FileReader(new File(path, propertyName));
                properties.load(reader);
                logger.debug("Property Loaded successfully");
            }
            return properties;
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * Returns key value corresponding to properyName.
     *
     * @param key the key whose associated value is to be returned.
     * @param propertyName String property name.
     * @return value corresponding to key by passed property.
     */
    public static Object getValue(String key, String propertyName) {
        logger.debug("Invoking getValue...");
        Properties properties = loadProperty(null, propertyName);
        return properties.get(key);
    }

    /**
     * Returns key value corresponding to properyName by specified directory.
     *
     * @param key the key whose associated value is to be returned.
     * @param propertyDir directory path of property.
     * @param propertyName String name of property.
     * @return object value of a key defined in property.
     */
    public static Object getValue(String key, String propertyDir, String propertyName) {
        logger.debug("Invoking getValue for key: " + key + ", directory: " + propertyDir + ", Property Name: " +
                propertyName + "...");
        Properties properties = loadProperty(propertyDir, propertyName);
        return properties.get(key);
    }

}

6.2. ResourceBundleUtil Java Source Code

/* Copyright (C) 2014, 2015 Sanjay Madnani
 *
 * This file is free to use: you can redistribute it and/or modify it under the terms of the GPL General Public License
 * V2 as published by the Free Software Foundation, subject to the following conditions:
 *
 * The above copyright notice should never be changed and should always included wherever this file is used.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
 * See the GNU General Public License V2 for more details. */
package com.sanjay.util;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Utility for getting i18n value from Properties file.
 *
 * @author SANJAY
 * @see ResourceBundle
 * @see Locale
 * @see MessageFormat
 */
public final class ResourceBundleUtil {
    private static final Logger logger = LogManager.getLogger(ResourceBundleUtil.class);

    /**
     * Private Construct to restrict object creation.
     */
    private ResourceBundleUtil() {
    }

    /**
     * Gets a resource bundle using the specified base name and locale.
     *
     * @param baseName - the base name of the resource bundle.
     * @param locale - the locale for which a resource bundle is desired.
     * @exception NullPointerException - if baseName is null.
     * @exception MissingResourceException - if no resource bundle for the specified base name can be found.
     * @return a resource bundle for the given base name and locale.
     */
    private static ResourceBundle getResourceBundle(String baseName, Locale locale) {
        logger.debug("Invoking getResourceBundle by Loading Bundle: " + baseName + "...");
        ResourceBundle bundle = ResourceBundle.getBundle(baseName, (locale == null) ? Locale.getDefault() : locale);
        logger.debug("Resouce Bundle loaded successfully");
        return bundle;
    }

    /**
     * Gets a string for the given key from baseName resource bundle. {@link #getResourceBundle}.
     *
     * @param key - the key for the desired string.
     * @param baseName - the base name of the resource bundle, a fully qualified class name.
     * @param locale - the locale for which a resource bundle is desired.
     * @exception NullPointerException if key is null.
     * @exception MissingResourceException if no object for the given key can be found.
     * @exception ClassCastException if the object found for the given key is not a string.
     * @return the string for the given key.
     */
    public static String getStringMessage(String key, String baseName, Locale locale) {
        logger.debug("Invoking getStringMessage for key: " + key + " and bundle: " + baseName + "...");
        try {
            ResourceBundle bundle = getResourceBundle(baseName, locale);
            return bundle.getString(key);
        } catch (NullPointerException | MissingResourceException | ClassCastException ex) {
            logger.error(ex.getMessage(), ex);
         // TODO throw your custom exception rather than returning null.
            return null;
        }
    }

    /**
     * Gets a string for the given key from baseName resource bundle and formats string with passed arguments.
     * {@link #getResourceBundle}.
     *
     * @param key - the key for the desired string.
     * @param baseName - the base name of the resource bundle.
     * @param locale - the locale for which a resource bundle is desired.
     * @param arguments - parameters for formatting string.
     * @exception NullPointerException if key is null.
     * @exception MissingResourceException if no object for the given key can be found.
     * @exception ClassCastException if the object found for the given key is not a string.
     * @exception IllegalArgumentException if the value of key is invalid, or if an argument in the arguments array is
     *                not of the type expected by the format element(s) that use it.
     * @return formated string for the given key.
     */
    public static String getFormatedMessage(String key, String baseName, Locale locale, Object... arguments) {
        logger.debug("Invoking getFormatedMessage...");
        try {
            ResourceBundle bundle = getResourceBundle(baseName, locale);
            return MessageFormat.format(bundle.getString(key), arguments);
        } catch (NullPointerException | MissingResourceException | ClassCastException ex) {
            logger.error(ex.getMessage(), ex);
         // TODO throw your custom exception rather than returning null.
            return null;
        }
    }
}

7. Proper Implementation

Here you can see that to deal with each property we just create an object which retrieves values from that property and hence here it is not required to load same property again and again for retrieving each key value.

7.1 PropertyUtil Java Source Code

/* Copyright (C) 2014, 2015 Sanjay Madnani
 *
 * This file is free to use: you can redistribute it and/or modify it under the terms of the GPL General Public License
 * V2 as published by the Free Software Foundation, subject to the following conditions:
 *
 * The above copyright notice should never be changed and should always included wherever this file is used.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
 * See the GNU General Public License V2 for more details. */
package com.sanjay.util.proper;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.text.MessageFormat;
import java.util.Properties;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Utility for Loading & Retrieving Properties values.
 *
 * @author SANJAY
 * @see Properties
 * @see MessageFormat
 */
public final class PropertyUtil {

    private static final Logger logger = LogManager.getLogger(PropertyUtil.class);
    private transient Properties properties;

    /**
     * Construct Object using the specified resourceBundleName.
     *
     * @param baseName - the base name of the resource bundle.
     * @exception NullPointerException - if resourceBundleName is null.
     */
    public PropertyUtil(final String baseName) {
        logger.debug("Invoking Constructor with baseName: " + baseName + "...");
        try {
            properties = new Properties();
            InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(baseName);
            properties.load(inputStream);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * Construct Object using the specified resourceBundleName.
     *
     * @param pathDirectory String directory of baseName.
     * @param baseName - the base name of the resource bundle.
     * @exception NullPointerException - if resourceBundleName is null.
     */
    public PropertyUtil(final String pathDirectory, final String baseName) {
        logger.debug("Invoking Constructor with path: " + pathDirectory + ", baseName: " + baseName + "...");
        try {
            properties = new Properties();
            Reader reader = new FileReader(new File(pathDirectory, baseName));
            properties.load(reader);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * Returns value corresponding to key.
     *
     * @param key the key whose associated value is to be returned.
     * @return value corresponding to key.
     */
    public Object getValue(String key) {
        logger.debug("Invoking getValue by passing key: " + key + "...");
        return properties.get(key);
    }
}

7.2 BundleUtil Java Source Code

/* Copyright (C) 2014, 2015 Sanjay Madnani
 *
 * This file is free to use: you can redistribute it and/or modify it under the terms of the GPL General Public License
 * V2 as published by the Free Software Foundation, subject to the following conditions:
 *
 * The above copyright notice should never be changed and should always included wherever this file is used.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
 * See the GNU General Public License V2 for more details. */
package com.sanjay.util.proper;

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Utility for getting i18n value from Properties file.
 *
 * @author SANJAY
 * @see ResourceBundle
 * @see Locale
 * @see MessageFormat
 */
public final class BundleUtil {
    private static final Logger logger = LogManager.getLogger(BundleUtil.class);

    private transient ResourceBundle resourceBundle;

    /**
     * Construct Object using the specified resourceBundleName.
     *
     * @param resourceBundleName - the base name of the resource bundle.
     * @exception NullPointerException - if resourceBundleName is null.
     */
    public BundleUtil(final String resourceBundleName) {
        logger.debug("Invoking 1 arg Constructor by bundle: " + resourceBundleName + "...");
        try {
            this.resourceBundle = ResourceBundle.getBundle(resourceBundleName);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // TODO throw your custom exception
        }
    }

    /**
     * Construct Object using the specified resourceBundleName and locale.
     *
     * @param resourceBundleName - the base name of the resource bundle.
     * @param locale - the locale for which a resource bundle is desired.
     * @exception NullPointerException - if resourceBundleName is null.
     * @exception MissingResourceException - if no resource bundle for the specified base name can be found.
     */
    public BundleUtil(final String resourceBundleName, final Locale locale) {
        logger.debug("Invoking 2 args Constructor by bundle: " + resourceBundleName + "...");
        try {
            this.resourceBundle = ResourceBundle.getBundle(resourceBundleName, locale);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // TODO throw your custom exception
        }
    }

    /**
     * Gets a string for the given key.
     *
     * @param key - the key for the desired string.
     * @exception NullPointerException if key is null.
     * @exception MissingResourceException if no object for the given key can be found.
     * @exception ClassCastException if the object found for the given key is not a string.
     * @return the string for the given key.
     */
    public String getStringMessage(final String key) {
        logger.debug("Invoking getStringMessage...");
        try {
            return this.resourceBundle.getString(key);
        } catch (NullPointerException | MissingResourceException | ClassCastException ex) {
            logger.error(ex.getMessage(), ex);
            // TODO throw your custom exception rather than returning null.
            return null;
        }
    }

    /**
     * Gets a string for the given key and formats string with passed arguments.
     *
     * @param key - the key for the desired string.
     * @param arguments - parameters for formatting string.
     * @exception NullPointerException if key is null.
     * @exception MissingResourceException if no object for the given key can be found.
     * @exception ClassCastException if the object found for the given key is not a string.
     * @exception IllegalArgumentException if the value of key is invalid, or if an argument in the arguments array is
     *                not of the type expected by the format element(s) that use it.
     * @return the formated string for the given key.
     */
    public String getFormatedMessage(final String key, final Object... arguments) {
        logger.debug("Invoking getFormatedMessage...");
        try {
            return MessageFormat.format(this.resourceBundle.getString(key), arguments);
        } catch (NullPointerException | MissingResourceException | ClassCastException | IllegalArgumentException ex) {
            logger.error(ex.getMessage(), ex);
         // TODO throw your custom exception rather than returning null.
            return null;
        }
    }
}

8. Project Source Code Repository Link

Click here to download/clone/view maven project source code of above utilities with JUnit test cases.
Note: Source code is with log4j2, Junit4 and it’s a maven project. You can directly download project and then run as maven clean and then maven install then you will see the status of test cases, In case to skip test cases while running project as maven install you can change setting in pom.xml file, here it is: (<skipTests>true</skipTests>)

Advertisements

About Sanjay Madnani

Software Developer.
This entry was posted in Tutorials and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s