Issue Details (XML | Word | Printable)

Key: SPR-5029
Type: Improvement Improvement
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Juergen Hoeller
Reporter: Pascal Alberty
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
Spring Framework

DefaultMessageSourceResolvable hashcode does not take arguments into account

Created: 23/Jul/08 10:34 AM   Updated: 25/Jul/08 02:10 AM   Resolved: 24/Jul/08 06:31 AM
Component/s: SpringCORE
Affects Version/s: 2.5.1
Fix Version/s: 2.5.6

Time Tracking:
Not Specified


 Description  « Hide

DefaultMessageSourceResolvable hashCode method only uses "codes" field and not "arguments" field preventing to create a correct cache key when using springmodules and @cacheable annotation on getMessage(MessageSourceResolvable messageSourceResolvable, Locale locale) method

The DefaultMessageSourceResolvable hashCode method:

public int hashCode() { return ObjectUtils.nullSafeHashCode(getCodes()); }

generateKey method of HashCodeCacheKeyGenerator class (springmodules) generate a key based on method arguments.
If the argument is a FieldError class (which extends ObjectError which extends DefaultMessageSourceResolvable), the key is not different if "arguments" field is different.



Juergen Hoeller added a comment - 23/Jul/08 11:16 AM

Well, this is not a bug as far as I see, since a hashCode() implementation is allowed to return a simplified hash key - even a constant value would be valid in principle! hashCode values need to be equal if the equals(Object) returns true... however, they may also be equal if equals(Object) returns false! Distinct integer values should be used in order to improve the performance of hash lookups, but otherwise hashCode values are not required to differ in the first place...

I would rather claim that the HashCodeCacheKeyGenerator strategy is a bit fragile here, given the relaxed hashCode() contract that java.lang.Object defines!

As for your specific use case: Why do you externally cache messages in the first place here? Spring's ResourceBundleMessageSource and ReloadableResourceBundleMessageSource classes perform quite extensive caching already... internal to the MessageSource. Are you using a custom MessageSource implementation there?

Juergen


Pascal Alberty added a comment - 24/Jul/08 01:40 AM

Thanks for your fast answer Juergen.

Concerning the hashCode implementation, I admit, it's to give a lot of responsability. It's for me a semantic question, what are the fields representing distinct Object. For me, Codes field is not enough. But could we give this kind of semantic responsability to the hashCode method. May be the equals method should also be modified.
The question is, what fields are semanticaly identified a DefaultMessageSourceResolvable ?

However, yes I'm using a specific MessageSource implementation which is, for a specific key, first take a look into a ResouceBundleMessageSource then into a kind of custom DataBaseMessageSource if not found in the first one.

My "dispatcher" is imlpementing the MessageSource interface and the results of the following methods are well cached (using SpringModules caching annotation):
getMessage(String key, Object[] arguments, Locale locale)
getMessage(String key, Object[] arguments, String defaultMessage, Locale locale)
but not for:
getMessage(MessageSourceResolvable messageSourceResolvable, Locale locale)
as explained if MessageSourceResolvable is a FieldError.

Thanks


Juergen Hoeller added a comment - 24/Jul/08 04:58 AM

I guess the fundamental question here is whether hashCode() is actually supposed to semantically identify its object. The original purpose of hashCode() is simply to choose an appropriate hash bucket in a hash map; such buckets are by their very nature designed to hold multiple objects, in particular ones with the same hash code. Hash codes are not reliably distinct; they're ints, so just imagine a large enough number of objects and hash codes are bound to overlap...

Following from that line of reasoning, HashCodeCacheKeyGenerator is inherently fragile. It relies on hashCode() distinctiveness assumptions that are not even demanded by the java.lang.Object documentation. We may refine DefaultMessageSourceResolvable's hashCode() implementation (doesn't hurt to take arguments into account, even if it will lead to more expensive hashCode calculation); nevertheless, it seems that HashCodeCacheKeyGenerator's strategy remains fragile.

Juergen


Juergen Hoeller added a comment - 24/Jul/08 05:07 AM

Actually, as you pointed out (which I haven't really noticed before), DefaultMessageSourceResolvable's equals(Object) implementation compares the codes field only, assuming that all such resolvables are effectively identifying the same message. This is questionable indeed... since arguments will lead to a different message String getting returned. I'll revisit this.

Technically it would be sufficient to revise the equals(Object) implementation here and keep hashCode() as-is. I suppose for the purposes of HashCodeCacheKeyGenerator, we still need to revise the hashCode() implementation as well...

Juergen


Juergen Hoeller added a comment - 24/Jul/08 06:31 AM

DefaultMessageSourceResolvable and also FieldError implement "equals" and "hashCode" such that all fields are respected now. This should generally be cleaner and also make hashCode-based caching strategies work as far as possible given the fragility of the hashCode contract.

This will be available in tonight's 2.5.6 snapshot (http://static.springframework.org/downloads/nightly/snapshot-download.php?project=SPR). Please give it a try and let me know whether it works for you...

Juergen


Pascal Alberty added a comment - 24/Jul/08 06:51 AM

Thanks. I keep you informed.


Pascal Alberty added a comment - 25/Jul/08 02:10 AM

Caching is working well now with snapshot spring-framework-2.5.6-20080724-566.zip

Thanks Juergen !!!