Share on:

Hacking the enum conversion in JSF 1.2_x

JUG LvivJUG Lviv
Сьогодні зіткнувся з тим що JSF 1.2 неправильно працює з енумами
Трохи пошукав і знайшов ось тут рішеня і навіть дет
Suppose you have an enum and some of its methods are overridden by concrete enum values:
In the example a custom isDifferent() method is overridden, but it could as well be the existing toString().
If you attempt to use such an Enum (the OTHER_VALUE to be precise) within a view in a JSF application (using the reference implementation, haven’t tried MyFaces), you’re likely to get an exception like this:
After some debugging it turns out that enum values with non-empty body are not handled properly. If you have a look at the com.sun.faces.application.ApplicationImpl class, there’s a method
Normally, when JSF is looking for a converter for an enum without overridden values, the targetClass parameter is the concrete enum class (it should be SomeEnum in our case, but it actually is SomeEnum$Something). The method first tries to lookup a base type converter, but there’s none for SomeEnum$Something (that’s a surprise!). So, in the next attempt, it searches for interfaces implemented by the targetClass – but nothing here again. As a last resort, the method goes through a collection of targetClass’s superclasses. This time a superclass is found – it’s the SomeEnum – so the
method is called with SomeEnum as the targetClass and SomeEnum$Something as the baseClass. Similar steps as above are now taken to find and appropriate converter for interfaces/superclasses of SomeEnum. As SomeEnum extends java.lang.Enum, the method is called recursively with null as the baseClass:
Finally, a matching converter is found for java.lang.Enum and an instance of javax.faces.convert.EnumConverter is created. Almost everything is fine, except for the fact that it gets the above null (passed as the baseClass above) injected as the target class in constructor parameter.
This causes the converter to throw the exception mentioned above when assuring that its target class is not null:
Unlike the problem description, the solution seems to be quite simple. What we basically need to do is to handle the case when JSF is looking for a converter for an anonymous inner class of an enum and swap the classes in the call to createConverterBasedOnClass(). So if the tagetClass is our enum and the baseClass is its anonymous extension, we should call:
otherwise we shouldn’t change anything and call:
To weave our solution into JSF we need to extend the com.sun.faces.application.ApplicationImpl class and override its createConverterBasedOnClass() method:
Now we need make JSF use the above implementation instead of the standard one. This is done by extending the com.sun.faces.application.ApplicationFactoryImpl and override the getApplication() method to simply return our FixedEnumConverterApplicationImpl:
The final step is to register a custom application factory in faces-config.xml:
So, this one worked for me. Of course you can simply avoid overriding methods in your enums or perhaps use MyFaces, which may not contain this bug. If you think this is a nice or bad solution, please leave a comment.