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
Last month I discussed the scenario wherein the values of static final variables are being compiled into the classes that reference them, thus requiring a recompile of the entire project for any changes to propagate. I stated that this behavior is (or was two years ago) different from one compiler to the next. JavaWorld reader Ethan Nicholas was kind enough to correct me on the matter:
This behavior is specified by the Java Language Specification, section 13.4.8. The gist of it is that references to primitive constant fields (static final with a constant primitive initializer) are compiled into other classes by value rather than by reference. What you probably saw was a constant number refusing to change, even though you were accustomed to constant strings changing -- this is correct behavior (according to the spec), as strings are not primitive and so must be passed by reference. It certainly should not vary from compiler to compiler -- if a compiler compiles a reference to a primitive constant, rather than just the value, it is in error.
A few of you commented to me that I should have made the GlobalValues object into a singleton. The term singleton comes from the realm of design patterns. A singleton object strives to ensure that it exists in only one instance and that
it offers a global point of access.
In essence, the GlobalValues object is a singleton. All of the tool's methods are static, and the internal Hashtable is static, so you can (and should) reference the tool via the class name. I demonstrated this in my article, and that is
how I intended the tool to be used. As some of you pointed out, however, I can go a couple steps further to make GlobalValues a singleton in the purest sense.
First of all, I should have declared the class to be final, as in:
public final class GlobalValues
This ensures that nobody can subclass the GlobalValues and override the functionality. A nice safeguard.
Second, I should have declared a private constructor:
private GlobalValues(){}
This ensures that no one can create instances of the object. At first, having numerous instances doesn't seem all that troublesome
because the internal Hashtable is static, which means all of the instances would share the same data. There is a nasty race condition, however: a static synchronized method does not synchronize across multiple instances of its class. For further reading on this, see Part 7 of Allen Holub's Java Toolbox series, "Programming Java threads in the real world."
A couple of you also suggested that I make the tool serializable and thus RMI-friendly. I don't see the practicality of this,
but it certainly can be done. The first thing I need to do is make all the internal data nonstatic. In the case of GlobalValues, the internal data is the lonely Hashtable. Once the internal data is nonstatic, it doesn't make any sense for the get() method to be static. We must now instantiate the tool in order to use it -- thus, the Value inner class doesn't need to be static either. Also, the JDBC driver loading block should be moved out of the static block
and into the private constructor. Now, nothing in GlobalValues is static.
In order to preserve the singleton nature of the tool, we must control its instantiation. We must make sure that only one
instance of the tool can be created, and that everyone uses that lone instance. In order to accomplish this, we will use yet
another design pattern, called Factory. A factory in the real world is a big building where merchandise is produced. We, the consumers, don't care how the factory
creates the merchandise; we're only interested in purchasing it. In the programming realm a Factory is an object that produces other objects.
In the case of our GlobalValues tool, we can make it a Factory of itself. This requires us to add in a couple more static entities. The first is a reference to an instance of itself:
private static GlobalValues self = null;
Making the constructor private has already ensured that no other class (except the tool itself) can instantiate a GlobalValues object. All we need now is to give the outside world access to a single internal instance:
public static synchronized GlobalValues getRef() {
if( self == null )
self = new GlobalValues();
return self;
}
And, don't forget to implement the Serializable interface:
public final class GlobalValues implements java.io.Serializable
Now the usage of the tool changes slightly. Here is the old way:
String value = GlobalValues.get( "MY_VALUE" );
And here is the new way:
String value = GlobalValues.getRef().get( "MY_VALUE" );
Here is the complete source of the new and improved GlobalValues tool.
In last month's column, there is a typo in the SQL of the load() method. Since I am using a PreparedStatement, the apostrophes around the question mark aren't necessary. Here's the correct line:
String sql = "SELECT VALUE, EXPIRE FROM GLOBAL_VALUES WHERE NAME >= ?";
Also, it's possible to get a NullPointerException from the get() method because I am not checking the return value of the load() method. If the requested value isn't in the database, then load() will return null, and you can't put null into a Hashtable. Here's a better way to do it:
value = load( name );
if( value != null ){
cache.put( name, value );
} else {
return null;
}
Anthony Eden dropped me a note to let me know that he expanded on my idea -- the way in which I solved the problem and implemented
the tool. He calls his version of the tool a PropertyManager. The most notable improvement in his version is how he abstracted the database layer so that the tool can get the key-value
pairs from anywhere; pluggable PropertyLoader modules allow you to access data in a standard database, a flat file, an XML document, and so on. Check it out at http://edens.simplenet.com/pmanager/.
Design patterns seem to be the underlying theme this month. I discussed a few above when I addressed comments from readers regarding last month's tool. And we're going to talk about yet another one in this part of my article, which is devoted to the new tool. Called Facade, this design pattern is a high-level interface that encapsulates many other subsystem interfaces. This month I'm applying Facade to the more popular JDBC classes. What does this do for us? It means you get the same JDBC functionality -- high-level database abstraction -- with a lot less typing. Your database-related code will thus be smaller and cleaner.