Tuesday, January 29, 2013

Keeping Track of App Settings in Java

Recently I was working on a Java application which required certain settings to be saved and reloaded, such as window positions, user data, etc. I thought it would be a great idea to use a centralized object which could keep track of the settings as many of them are shared between objects. As I got to thinking about how I could implement this centralized class, I imagined an XML file being saved to the drive which I would parse using a SAX parser, manually picking out the fields I wanted, and overall taking a ton of work. Everytime I wanted to add a new setting it involved going into my Settings class and adding a field and some custom SAX parsing code.

However, I realized that there was a much better way to do things, and that's to take advantage of the default Java Serialization API. This automatically handles the transfer of a Java object over a stream, which in my case are file streams so I can save and load the class. I also realized that rather than trying to push for efficiency and having the code completely valid by compile time, I would switch to a runtime management type approach. This involved Using a simple HashMap which would match objects with string key identifiers.

To take advantage of Java's built-in serialization you basically have to do one thing: Have your class implement the Serializable interface. This interface has no methods you need to override (though there are methods you can override to customize the serialization process) and is basically a dummy interface to let Java know that this class can be serialized.

It's also not a bad idea to add a serialVersionUID static final long field to your class. This field is used to verify that the sender and receiver are using compatible classes. It's not a requirement that you implement this as Java can "calculate" a default value, though it is strongly recommended as there may be quirks in individual implementations which may result in ocassional problems.

Here's a snapshot of my current Settings class. It's significantly simpler to use and manage than my initial attempt.

import java.io.Serializable;
import java.util.HashMap;

/**
 * Keeps track of application settings using Java's Serialization API. All properties being tracked
 * must implement the Serializable interface.
 * 
 * @author Andrew
 * 
 */
public class Settings implements Serializable
{
 private static final long serialVersionUID = 1234567890L;

 private HashMap<String, Serializable> _data;

 /**
  * 
  */
 public Settings()
 {
  _data = new HashMap<String, Serializable>();
 }

 /**
  * Tries to get a property.
  * 
  * @param name
  * @param default_value
  * @return the property value or default_value if no value exists.
  */
 public Serializable getPropertyOrDefault(String name, Serializable default_value)
 {
  if (_data.containsKey(name))
  {
   return _data.get(name);
  }
  else
  {
   return default_value;
  }
 }

 /**
  * Sets a property.
  * 
  * @param name
  * @param value
  * @return this
  */
 public Settings setProperty(String name, Serializable value)
 {
  _data.put(name, value);
  return this;
 }

 /**
  * 
  * @param name
  * @return true if the property exists
  */
 public boolean hasProperty(String name)
 {
  return _data.containsKey(name);
 }
}

Transmitting this object is done using Object streams such as the ObjectInputStream and ObjectOutputStream. For my purposes, I simple redirect the input/output to a file to save the settings to the filesystem, though you could do some fun stuff like send the settings across a network and synchronize the settings across different computers.

To write to a file:

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("settings.cfg"));
out.writeObject(settings);

To read back from a file:

ObjectInputStream in = new ObjectInputStream(new FileInputStream("settings.cfg"));
Settings settings = (Settings) in.readObject();

Thank goodness for Java's Serialization API saving me a bunch of time and effort.

No comments :

Post a Comment