I've just discussed Billy's tips for some J2EE programming model extensions in WAS. There are a couple of other recent items on his blog that are especially good and worth noting.
Those of us that have used JMS a lot have learned: Do not depend on message order! Just because you write messages out in a certain order doesn't mean they'll be received in the same order, because the messages can (not will, under ideal circumstances won't, but can) get mixed up between points A and B. This is a recurring issue because app developers tend to assume order is preserved, superficial testing confirms that order is preserved, and app integration designs (by plan or assumption) tend to depend on order being preserved. The classic hypothetical example is three messages to create, update, and delete a record: What if delete is received first, then create? What if update is received after delete, or before create? Bottom line: Don't depend on order.
In Total JMS Message ordering, Billy gives some nice compact examples of why messages get out of order. I'll give another: different paths--network paths, or messaging paths caused my intermediate routers and transformers--between the sender(s) and receiver(s). If the first message takes a sufficiently slower path than the second message, they arrive out of order.
In Don't use ThreadLocal!, Billy points out (and gets lots of comments) that code running in a J2EE app server shouldn't depend on a Java thread's local variables. The issue is that threads in an app server are shared resources, so you only get one for a short time to do a little work. Then when you're ready to do some more work, you may get a different thread, in which case any values you stored in the last thread are now gone. Or you get the same thread again, but another client may have used that thread since the last time you did and may have changed the values. So don't depend on them.
A side issue that spawned is: What if code only uses the thread local variables while using a single thread, during a synchronous call? (Which is probably the only way ThreadLocals should ever be used anyway.) This will work, but Billy points out that this will still fail if Java 2 Security is enabled (as it should be), because security will block code from messing with threads' internal state. If you've got code or frameworks that don't work correctly when security is turned on, that's not so good.
Sometime, I need to talk about an even more general theme: Don't spawn your own threads (Thread#start() or Runnable#run()) in J2EE. It's bad! But that'll have to wait 'til another time.