Compare and contrast:
String msg = getUserMessage(USER_NOT_FOUND, profile, pRequest); addFormException(new DropletFormException(msg, USER_NOT_FOUND);
with
String msg = getUserMessage(USER_NOT_FOUND, profile, pRequest); addFormException(new DropletException(msg, USER_NOT_FOUND));
The first example is incorrect. DropletFormException takes in a property path as the second argument, so that the designer can mark the property as failed (i.e. by making the login form field red). In the three argument form, it's the third argument that is the message key, so that i18n can be done on the actual page using droplets, so the designer can override a message key for a specific page.
(Which means that even
addFormException(new DropletFormException(msg, getAbsoluteName(), USER_NOT_FOUND);
is wrong. The second argument needs to be getAbsoluteName() + ".login", to include the property.)
Of course, once I realized what I'd done, I realized how horrible it would be to find and fix all of these. Worse, I'd have to review the code to make sure this kind of error wouldn't creep in again.
Using PMD and XPath Explorer, I can flag the two argument form of DropletFormException with an XPath expression:
//Name[@Image="DropletFormException" and count(following-sibling::Arguments/ArgumentList/Expression) = 2]
And suddenly everything becomes clear. I can include these in the custom rules for the project and I will never have to think about this again. Thank you PMD.