Spotted this nasty little quirk today – not for the first time. Every time I see it I want to get angry at someone but I’m not quite sure who.
What’s wrong with this list of properties?
private String name; private String address; private String eMail;
Nothing immediately obvious perhaps. Here are the standard generated getters and setters (signatures only):
public String getName(); public void setName(String name); public String getAddress(); public void setAddress(String address); public String getEMail(); public void setEMail(String eMail);
All perfectly valid. Now, given these auto-generated getters and setters, what names would standard bean introspection derive?
name address EMail
Hang on, what happened there? How did my eMail property turn into EMail?
It’s the result of a quirk of the Java Beans spec. Section 8.8 if you’re interested. The methods getAddress() / setAddress() correspond to the property ‘address’ in the fairly obvious way. The first letter is converted to lower case for the sake of standard Java naming conventions. However:
to support the occasional use of all upper-case names, we check if the first two characters of the name are both upper case and if so leave it alone.
And this catches us out. The times I’ve seen this in the past (and it’s been a few now), the resulting error has been hard to track down. Partly because it’s in someone else’s code (Struts, Spring, Apache BeanUtils etc) but mostly because everything else works. Unless you know this particular quirk, there’s nothing that makes this one field special.
What’s more, it usually fails silently. No exception thrown or warning logged. We pass in a field called ‘eMail’ to something dependent on bean introspection. It checks the bean, finds no corresponding method and so discards it.
As an example, if the above is in a Struts ActionForm, and you pass in
eMail = [email protected]
as a HTTP POST form parameter, it will simply not marry up with anything in your form bean and the eMail field will remain null.
Apache Commons BeanUtils.populate() does the same. If you use this, the eMail property is simply discarded. No warnings, no nothing. So this ‘feature’ potentially exists in everything that has a dependency on Apache Commons BeanUtils. Um, that’s pretty much every open source library for Java isn’t it?
Fortunately, Spring handles this a little better. If you attempt to inject a value into a non-existent property, it will tell you and refuse to start. At least you know there’s a problem somewhere. But in the cases where no warning is given, this bug can lurk. It will hang about for months without being noticed and then be elusive once you become aware of it.
Now, to the question of who I should be angry at. Should I be angry at myself and my colleagues for introducing a field with such a daft name in the first place? Or should I be angry at whoever at Sun thought that supporting all upper case property names was a good idea?
In defence of me and my colleagues, we didn’t necessarily choose these names for ourselves. I remember in one case the names being chosen by a third party company as sensible URL parameter names. In another, they were passed in via a .NET SOAP service. Still, we could have sanitised, tweaked, fixed, whatever. And we would have done, if it had been obvious that these were duff identifiers.
So I am angry at the guys at Sun. They came up with a neat elegant solution dependent on a simple naming convention. Then they took that neat elegant naming convention and hacked it for people who chose to ignore it. It’s the hack I don’t like. The exception to the rule. Have a naming convention or don’t. But if you do have one, don’t stick some obscure fudge in there. Hacks and fudges will catch up with you and bite you in the bottom.