Using Program Parameters in Java Part-3
Databases
Why not store parameters in a database? If your application is already using a database, you might consider defining a special table for this purpose. To hold key-value pairs you need two columns of type char: “key” and “value”. If you want a really flexible solution you should consider making a small (Swing or web?) application that can be used to insert and update the data in this table.
Where property files are fine for utility type and stand-alone programs, a database could be a better solution for applications with many users, for example web applications. To avoid access to the database every time there’s a need for a parameter value, you could load the parameters on system startup, and then keep them in a Properties or HashMap object held by a singleton. When (if) you update a parameter value, you’d update the database and also the HashMap. To do this properly you must synchronize access to the HashMap.
Hiding the implementation
As you can see there are many possibilities for storing parameters. When you choose one of them for your application, you might want to start with a simple solution, where you get the parameters from a property file, for example. Later you might want to read the parameters from a database, so it’d be nice if you could hide the implementation details of how you actually get the parameters. Let’s define an interface as simply as possible. What we need is a method with this signature:
public String getValue(String parameter);
If you put the method in a singleton class (you only need one instance since there is no state to preserve) you may define the method to be static:
public class Parameters {
. . .
public static String getValue(String parameter) {
. . .
To get your parameters use a method call like this:
String value = Parameters.getValue("mykey");
No implementation is visible, right? The way to code the singleton could be like this, if we chose to use a property file:
package dk.hansen.playground;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import org.apache.log4j.Logger;
public class Parameters1 {
private static Parameters1 instance;
private Properties p;
private static final String FILENAME = "my.properties";
private static Logger logger =
Logger.getLogger(Parameters1.class.getName());
private Parameters1() {}
/*
* Get the only instance. If not created yet
* then read the default file and create the instance
*/
public synchronized static Parameters1 getInstance() {
if (instance == null) {
logger.info("Creating new instance");
instance = new Parameters1();
instance.p = getParameters(FILENAME);
}
return instance;
}
/*
* Read the specified Properties file
*/
private static Properties getParameters(String fileName) {
Properties q = new Properties();
try {
q.load(new FileInputStream(fileName));
} catch (FileNotFoundException e) {
logger.error("File not found: " + fileName);
e.printStackTrace();
} catch (IOException e) {
logger.error("IO error on file: " + fileName);
e.printStackTrace();
} catch (RuntimeException e) {
logger.error("Error on file: " + fileName);
e.printStackTrace();
}
return q;
}
public static String getValue(String parameter) {
String value = (String) getInstance().p.get(parameter);
if (value == null) {
logger.warn("No value found for " + parameter);
value = "";
}
return value;
}
}
I’ve also thrown in some simple error handling using Log4J because we all know that this can not be left out in a professional application!
When the getValue method is used the first time the property file will be read and the Properties object “p” will be filled with the key- value pairs from the file. Since the constructor is private the file will only be read once. Here are a few lines to test the class:
String s = "parm1";
String v = Parameters1.getValue(s);
System.out.println("Value of " + s + ": " + v);
We’ve hardcoded the name of the property file in the class, which is not very flexible. We’ll therefore add another method, initialize, that allows us to enter the filename. To handle this we introduce two variables currentFile and nextFile, and change a few lines of the code:
. . .
private static final String FILENAME = "my.properties";
private static String currentFile = "";
private static String nextFile = FILENAME;
. . .
public synchronized static Parameters1 getInstance() {
if (instance == null) {
logger.info("Creating new instance");
instance = new Parameters1();
instance.p = new Properties();
}
if (!currentFile.equals(nextFile)) {
instance.p = getParameters(nextFile);
currentFile = nextFile;
}
return instance;
}
public static void initialize(String fileName) {
nextFile = fileName;
}
. . .
Note, that to avoid creating more than one instance of Parameter1 we always get the instance by calling the synchronized method getInstance.
Before we use the getValue method we’ll have to make a call to initialize:
Parameters1.initialize("my2.properties");
String s = "parm1";
String v = Parameters1.getValue(s);
System.out.println("Value of " + s + ": " + v);
We can improve the code even more. If we want to facilitate unit testing of the application using the parameter values, it’d be nice to be able to stick in key-value pairs without actually needing a file:
public static void setProperties(Properties q) {
// Avoid reading from a file
nextFile = currentFile;
getInstance().p = q;
}
Here’s how we may set our own key-value pairs:
Properties h = new Properties();
h.setProperty("parm1", "valueA");
h.setProperty("parm2", "valueB");
Parameters1.setProperties(h);
