Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
However, for server applications, where often a GUI-deficient property file set controls the configuration, changing configuration parameters without restarting the application is more desirable -- though more challenging.
For example, suppose your application emits different log-message levels depending on the logLevel parameter's value. During normal operation, you'd set logLevel to the lowest value for best performance. But if something unusual happens, you might want to change logLevel to a higher value for debugging purposes. In the process, you don't want to leave your hundreds, maybe thousands, of users
stranded.
As another example, you might want such capability during your servlet development, where you lack control when your servlet loads or unloads. If new properties load automatically, you won't need to stop and start the application server to change one servlet's configuration.
A typical, full-blown solution to this problem exposes the application properties through RMI (Remote Method Invocation) or HTTP. That way, you can manage the application configuration at runtime with either a Swing-based GUI or the Web interface. Of course, implementing such a solution requires much effort, and such areas as application security requires your utmost attention.
In this tip, I'll show you a much simpler solution that uses auto-reloadable property files. Thomas E. Davis proposed a similar solution in "How to Easily Reconfigure Your Applications -- While They're Running," where he used a database to store the reloadable properties.
You can manage application properties with Java's Properties class in its java.util package. You can use the Properties class to load application properties from any InputStream, such as a file, a jar archive, or a network connection. A plain-text properties file is commonly used. The following code
shows how you use a properties file:
1 import java.util.*;
2 import java.io.*;
3
4 public class Example {
5 private String emailAddr;
6 private int logLevel;
7
8 public Example() throws IOException {
9 loadProperties();
10 }
11
12 public String getEmailAddress() {
13 return emailAddr;
14 }
15
16 public synchronized void setEmailAddress(String email) {
17 emailAddr = email;
18 }
19
20 public int getLogLevel() {
21 return logLevel;
22 }
23
24 public synchronized void setLogLevel(int level) {
25 logLevel = level;
26 }
27
28 private void loadProperties() throws IOException {
29 Properties props = new Properties();
30 InputStream in = new
FileInputStream("example.properties");
31 props.load(in);
32 in.close();
33
34 setEmailAddress(props.getProperty("emailAddress"));
35 setLogLevel(Integer.parseInt(props.getProperty
("logLevel")));
36 }
37 }
The example.properties file looks like this:
1 emailAddress=admin@corporate.com 2 logLevel=4
As shown, the properties file loads when the object instantiates. You can easily change either logLevel or emailAddress by modifying them in the properties file. However, changing the properties file after object creation does not affect that
object's attributes. You must restart your application to make the new properties effective.
Note that in the example above the setters are made public, which lets you change the properties programmatically. This might or might not be desirable depending on your specific situation. If you plan to expose the properties via RMI or HTTP and use a Swing-based GUI or Web interface to manage the application, you should leave them public. On the other hand, using this tip's technique, you should disallow programmatic property changes by making properties setters private or protected, because the programmatically changed properties will be overridden when you auto-reload them from the properties file.
Normally, you can put property files on a relative or absolute path. But if you desire more location independence and flexibility,
you can also put them on the CLASSPATH and get an InputStream like this:
this.getClass().getClassLoader().getResourceAsStream
("my.properties");
The Example class provided in the source code's util/test directory demonstrates this technique; see jw-javatip125.zip.
To use the new values from a modified properties file without restarting, you just reload the properties whenever the file changes. The solution is simple: you monitor the properties file, and if the file changes, you reload the properties. Therefore, you need a file monitor to detect file changes and a callback interface to notify any changes to a monitored properties file.
However, two problems remain:
You satisfy the first problem by delegating the monitoring to a dedicated file monitor. You satisfy the second by using two
classes from the JDK 1.3 java.util package: Timer and TimerTask.
FileChangeListener.java in the source code defines the following callback interface:
1 public interface FileChangeListener {
2 public void fileChanged(String fileName);
3 }
The file monitor must implement two public methods:
addFileChangeListener, which adds a file with its callback listener to the file monitor for monitoringremoveFileChangeListener, which removes a monitored file/callback pair from the file monitor
Here's the relevant code for FileMonitor:
1 public void addFileChangeListener(FileChangeListener
listener,
2 String fileName,
3 long period)
4 throws FileNotFoundException {
5 removeFileChangeListener(listener, fileName);
6 FileMonitorTask task = new FileMonitorTask(listener,
fileName);
7 timerEntries.put(fileName + listener.hashCode(), task);
8 timer.schedule(task, period, period);
9 }
10
11 public void removeFileChangeListener(FileChangeListener
listener,
12 String fileName) {
13 FileMonitorTask task = (FileMonitorTask)
14 timerEntries.remove(fileName
15 +
listener.hashCode());
16 if (task != null) {
17 task.cancel();
18 }
19 }
The jw-javatip125.zip file's FileMonitor.java contains this implementation's full source code.