Wednesday, January 11, 2012

Handling poison JMS messages in Glassfish - infinite loop WTF?

I found this post useful for understanding problems with handling "poison messages" in message-driven beans:

http://weblogs.java.net/blog/felipegaucho/archive/2009/09/24/handling-poison-messages-glassfish

The gist of it is that even if you think you caught an exception, your transaction still might roll back and cause the JMS message to be re-delivered.  (The MDB is an EJB, which will default to container-managed transactions, equivalent to having REQUIRED on each method.)

There are two small caveats to add:

1. The specific issue with JPA is that certain persistence exceptions mark the transaction for rollback, even if the exception is actually caught.  It matters not whether that JPA activity is happening directly in onMessage() or in a "sub-transaction".

2. Simply annotating another method in the same MDB with @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) does not actually create a separate transaction context.  If you are calling a method locally within the same EJB, "this" refers to the object itself, while the transaction behavior lives in the EJB proxy wrapper.  (See http://stackoverflow.com/questions/427452/ejb-transactions-in-local-method-calls)  So you actually have to put this transaction in a separate EJB and inject it into the MDB with @Inject or @EJB annotations.

And now for the "WTF" part.

The thing that really surprised me is the retry behavior.  If the MDB throws an exception, and the transaction rolls back as a result, Glassfish recognizes that there was an exception and will only re-deliver the message once.  There is some setting somewhere that controls how many retries are attempted, I believe.  (Haven't found it.)

If you catch an exception that marked the transaction for rollback (or the transaction was marked for rollback programmatically), the transaction still rolls back, and the message is redelivered.  However, the rollback without exception does not fire Glassfish's retry counter, so you end up in an infinite loop.

Either way the solution is the same, but still -WTF!

Saturday, January 07, 2012

equals and hashCode on @Entity classes: just say no

I've come to the conclusion that you should avoid defining equals and hashCode on JPA @Entity objects unless you have a really good reason to.

Doing it right is non-trivial, with all the gotchas and caveats.  First, the logistics:
  • You can't use the primary key because multiple unsaved objects all have a null PK and would be "equal".  
  • Using the PK with object identity as fallback means an object won't be equal to itself before and after being persisted, and hash-based collections won't work properly if the hash code changes during the collection's lifespan.
  • A unique business key could work, if that's an option - but you have to remember to maintain the equals and hashCode method if those properties or relations change.  Also if that key isn't immutable, you have a similar problem as with PK + fallback above.
  • Creating a UUID in the constructor just to make equals and hashcode work is... yucky.
  • Often these methods are insufficiently covered by unit tests.
Second: there's the deeper question of what should "equals" mean in the context of a mutable business entity?  What comparison makes sense is often context-dependent.  Sometimes you might want to compare based on primary key, other times you might want to compare on some other property.

So what's the worst thing that happens if you just don't do anything, and take the default based on object identity?

Usually, you won't even miss these methods.  The default implementation will work fine unless you're trying to compare objects loaded in two different persistence sessions/transactions,with either equals() or contains().   I've found this is the exception case - much of my collection manipulation in practice is manipulating objects all loaded in the same session to hand off to the view layer, so HashSet recognizes duplicates and contains() works just fine.  And I almost never use an entity as a key in a hashmap.

The price you pay is more verbosity when you do need to compare objects between a session-scoped cache and the current request.  You have to remember that obj1.equals(obj2) doesn't work, and needs to be replaced with obj1.getId().equals(obj2.getId()).  Contains is a bit more verbose, and a utility method like containsById(collection, obj) might be helpful.  Some people will say it's confusing that equals doesn't "just work" but I find it less confusing to be explicit about what you're comparing on - and less confusing than a broken or unmaintained equals() method.

Finally, if this were C#, we wouldn't even have this discussion.  With closures and LINQ extension methods we would just say "collection.Where(obj => obj.id = foo.id)" and be done with it!

Related links:
http://community.jboss.org/wiki/EqualsAndHashCode

http://onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=3

http://stackoverflow.com/questions/1929445/to-equals-and-hashcode-or-not-on-entity-classes-that-is-the-question

http://stackoverflow.com/questions/1638723/equals-and-hashcode-in-hibernate

https://forum.hibernate.org/viewtopic.php?t=928172

http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html: "For mutable objects, the answer is not always so clear. Should equals() and hashCode() be based on the object's identity (like the default implementation) or the object's state (like Integer and String)? There's no easy answer -- it depends on the intended use of the class... It is not common practice to use a mutable object like a List as a key in a HashMap."

http://burtbeckwith.com/blog/?p=53

Tuesday, January 03, 2012

Code coverage - unintended benefit

Unit test coverage is not a panacea.  You can reach 100% unit test coverage without a single assertion about outputs or business logic, in which case the tests aren't that useful.

Or are they?  Just the act of executing every single line and branch of code does provide one important value: it demonstrates that you can show what inputs or conditions trigger which parts of the code.

More importantly, the act of getting there helps reveal sections of code and conditionals that you might not even need.  Having a coverage target also gives you incentives to stay "clean" and avoid gratuitous not-null checks or try/catch blocks, two pet peeves of mine.  (I've seen too many not-null checks that looked defensive at first but in reality just kicked the can down the road, further obscuring the real problem.)

The other benefit I've found is that a coverage goal encourages refactoring that you should be doing anyway, because well-factored code is easier to unit test.  For instance I had one JSF/JPA application where I was accessing JPA EntityManagers directly in the JSF managed beans.  This made unit tests on the managed beans annoying because I had to mock out EntityManager.createQuery and dozens of Query.setParameter calls for each JPA action.  By pulling the JPA actions into a separate DAO layer, I could just mock a single call to myDAO.getStuff(arguments).  Plus, after isolating the DAO, I could then write an integration test on the DAO hooking up to a real database.

Other related links:
http://www.wakaleo.com/blog/316-code-coverage-as-a-refactoring-tool
http://codebetter.com/patricksmacchia/2009/06/07/high-test-coverage-ratio-is-a-good-thing-anyway/

Primefaces AJAX callbacks: onstart vs. onclick

I just learned the hard way that onstart and onclick are not the same thing.

In particular, a "return ..." has very different semantics in both cases.

Consider this code:
<p:commandLink action="#{bean.method}" onstart="return func()" ...>

If "func()" return false, this code will abort the AJAX request and bean.method() won't get called.
If "func()" returns true, the AJAX request processes.
If you replace onstart with onclick, the AJAX request will abort even if func() returns true.

That's because the Primefaces puts the code to generate the AJAX request in the onclick handler, pre-pending your code from the p:commandLink onclick before it.  If your code returns, the AJAX request never gets sent.