I'm been teaching some J2EE design
patterns and identity services both lately, and drawing from a lot of architect/designer experience in the room, as well as a few observations of my own. So let me start one level up:
The consequence to programmatic security, done bit by bit, is that you end up spreading related logic all over the place. With some refactoring, you can of course centralize this, and treat authentication and authorization (just call it A&A from now on) as a cross-cutting service. Still, you're writing code to protect code, meaning you get to recompile when your policies change, or design a suitable pattern for reading from files that must be maintained (not unlike declarative security after all), or whatever. As a rule of thumb, I think of recompilation as anathema to ease of maintenance, so that's my particular bias.
Declarative security is better, in my book, for a few reasons. a) It lets the container do work it was designed to support;
b) the XML syntax carries you right into being smart, by thinking of roles and policies rather than people and which services they want. This layer of abstraction is important, even for small development groups. The only reason I can think to poo-poo is if you know your user base won't grow and you plan to quit if it does;
c) get a decent XML editor, one that browses tagged objects in a clean, collapsible way, and the size of web-apps.xml loses its first and foremost burden of being tedious to edit;
d) most importantly, when either the declarative or programmatic stuff gets unwieldy, it's far easier to factor declarative stuff into -- ta daaaa! -- some form of identity service.
The missing strategy in the question is exactly that -- a standalone process to handle A&A on behalf of the entire container. These are alternatively called identity servers or directory access management servers, but all they are is a directory server fronted by a web server using serlvet/JSPs just to map the authenticated to their authorized services.
Boy I hope Map doesn't see this, but with a simple XSLT widget you could probably take a massive A&A declared scheme and import it into an identity server -- with programmatic controls, you have a bit of work to do to make the switch.
HTH