September 10, 2004
Rotten Statics

We had rather an obscure issue today.

Rather against my advice, we have a class to keep all our string constants in - Constants.java. I wanted to keep these strings either in the classes to which that naturally belong, or in a configuration file if they were likely to change.

What can I say? I was outvoted. ;-)

Anyway, Constants.java consists of nothing but a bunch of lines like this:

public static final String PRINCIPAL_SESSION_ATTRIBUTE = "principal";

At the last minute before going live, the value of one of these strings changed. We ran our dist target, and installed.

One of our classes, let's call it Skippy.java, refers to this string - and we were finding that it was picking up the old value. We were, to say the least, perplexed. We removed the web application completely from Tomcat, and deleted WARs and classes from all over the shop before reinstalling the app. No dice - it still picked up the old value. We bounced Tomcat, which didn't help. We rebooted the server, ditto.

We even decompiled Constants.class. It contained the new value.

In the end, I tried decompiling Skippy.class. Lo and behold, there it was - the old value. WTF was it doing in there? It should have referred to the value in Constants.class, shouldn't it? Why did it have its own copy? And if Skippy.class was supposed to have its own copy of the literal, why didn't Ant recompile it for us?

Anyway, we only had to run ant clean dist, and install the WAR file we got from that, and all was well.

Lessons learned today:

  • Really don't use a bloody constants class.
  • Make your dist target dependent on your clean target.
  • Nobody likes it when you tell them "I told you so". ;-)

Posted to Java by Simon Brunning at September 10, 2004 04:40 PM
Comments

How about storing them in the database? Always works for me.

Posted by: Andy Todd on September 10, 2004 05:31 PM

i saw the exact same issue, and went through many of the same steps. our application is exploded in the filesystem, so we hoped to be able to change one class and not have to redploy the whole thing. this interesting side effect caused us the same headaches of having to redeploy anything that may have used our Constants class.

1. taking "final" off keeps it from happening, but also then doesn't protect it from accidental changes.

2. using new String("principal") would have avoided the actual value being inlined, since it's more dynamic.

3. externalizing the strings in the Constants object into a props file can fix this as well, and allows reconfiguration without recompilation.

this is an odd case where the compiler inlines the actual value instead of the normal reference to the object. I'm not sure ant should have been expected to recognize that to know to recompile. everything else is dynamically linked at runtime, so it normally wouldn't have required a recompile.

Posted by: john flinchbaugh on September 10, 2004 06:14 PM

Dude, ant and javac do pretty much NO dependancy analysis. There is no -MM for javac that will let you actually figure out the real dependancies, even the static ones. Simple fact is, if you change Constants.java, every class that refers to it must be recompiled. A real build system would figure that out for you. Unfortunately, Java has no such thing (available for C since the 80s).

Posted by: Dave on September 10, 2004 08:55 PM

I've been caught by this before, too. It's a real killer when a vendor provides their own version of a "standard" API with all the right methods and variables, but different values for a few key constants.

From the JLS, section 13.4.8, "The best way to avoid problems with 'inconstant constants' in widely-distributed code is to declare as compile time constants only values which truly are unlikely ever to change."

http://java.sun.com/docs/books/jls/second_edition/html/binaryComp.doc.html#45139

Section 15.28, explains what is and isn't compiled into a class as a literal value:
http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5313

Posted by: Alan Green on September 10, 2004 11:31 PM

1. Ant has a depend task that does calculate dependencies when you change a class.
Quicker than doing a full rebuild.

2. Stuff for distribution to a live server really, really should be compiled from a fresh checkout from source control though - thereby forcing a clean build every time.

3. Constants.java, I remember the meeting at work where we all decided they were a crap idea and scrapped them - joy. :-)

Posted by: Darren on September 12, 2004 11:05 PM

There *is* a build system for Java that (I think) would handle this: it's called Javamake (http://developers.sun.com/dev/coolstuff/javamake).

Posted by: Neil Greenwood on September 13, 2004 09:06 AM

Simon, I bet it was not so much the fact you got to say "I told you so" as much as the look of glee and self-richeousness on your face that nearly got you lynched at work ;-)

Posted by: Mark Matthews on September 13, 2004 09:17 AM

Yup, I think that fragging the build directory and doing a rebuild is the safest way.

Nuke the site from orbit. It's the only way to be sure. ;-)

Thanks for the tips, guys.

And Mark - I *know* that people who are always right can be annoying, but I can't help it. ;-)

Posted by: Simon Brunning on September 13, 2004 10:32 AM

Exposing any constant from any class can cause this issue.
I don't see how putting constants in meaningful classes would help with this issue.

This makes your I told you so wrong.

I think you should do real builds from a clean directory to prevent other potential issues.

I would say you learned less than would be ideal.


Posted by: Brendan Johnston on September 13, 2004 10:41 PM

Hey, lighten up, Brendan. ;-) Yes, I realise that *all* constants are potentially affected by this - but in our case, the constant was only used in one place, so having it in the client class *would* have helped.

But as I said: "Make your dist target dependent on your clean target" is the real fix for this.

Posted by: Simon Brunning on September 14, 2004 08:49 AM
Post a comment
Name:


Email Address:


URL:



Comments:


Remember info?