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:
1 | 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:
1 2 3 4 | public class Parameters { . . . public static String getValue(String parameter) { . . . |
To get your parameters use a method call like this:
1 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 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:
1 2 3 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | . . . 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
:
1 2 3 4 | 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:
1 2 3 4 5 | 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:
1 2 3 4 | Properties h = new Properties(); h.setProperty("parm1", "valueA"); h.setProperty("parm2", "valueB"); Parameters1.setProperties(h); |