On jdk1.6, apps that use custom lifecycles (such as
PartialTraversalLifecycle) and phase listeners (such as shale remoting's
phase listener) are failing due to unintentional redundant phase
listener registration. I've determined the cause.
ConfigureListener.configure(LifecycleBean config) has the following loop:
for (Iterator<String> iter = factory.getLifecycleIds();
iter.hasNext();) Here is LifecycleFactoryImpl.getLifecycleIds():
public Iterator<String> getLifecycleIds() {
return lifecycleMap.keySet().iterator();
}
With jdk1.5, the lifecycleIds returned are "DEFAULT" and
"com.sun.faces.lifecycle.PARTIAL", in that order. With jdk1.6, the order
is reversed. Our code in
PartialTraversalLifecycle.parentHasListenerOfClass is counting on the
parent lifecycle ("DEFAULT") coming before itself
("com.sun.faces.lifecycle.PARTIAL"). Specifically,
PartialTraversalLifecycle.addPhaseListener only adds the phase listener
to the parent if the parent does not already have the listener. But the
default lifecycle has no such logic (at least not in the versions prior
to jsf 1.2_05). So with jdk1.6, the reversing of the order causes a
redundant phase listener registration.
The fix/workaround attached ensures redundant phase listeners are
removed in PartialTraversalLifecycle.execute. It considers phase
listeners of the same type redundant. Note that this is, generally
speaking, a wider definition of redundancy than the logic employed in
jsf 1.2_05, which effectively tests for redundancy using the equals
method of the phase listeners. (Shale remoting's phase listener, for
instance, does not override equals, so only the exact same instance is
considered redundant.) Using this slightly different definition of
redundancy in PartialTraversalLifecycle.execute maintains consistency
with PartialTraversalLifecycle.addPhaseListener and ensures that phase
listeners are tested for both defintions of redundancy as of jsf 1.2_05.
Index: code/run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java
===================================================================
--- code/run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java (revision 461)
+++ code/run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java (working copy)
@@ -62,13 +62,27 @@
public class PartialTraversalLifecycle extends Lifecycle {
private Lifecycle parent = null;
+ private boolean redundantPhaseListenersRemoved = false;
public PartialTraversalLifecycle(Lifecycle parent) {
this.parent = parent;
}
-
public void execute(FacesContext context) throws FacesException {
+ if (!redundantPhaseListenersRemoved) {
+ PhaseListener [] listeners = getPhaseListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ //test if listeners[i] has any duplicates later in the array,
+ //and, if so, remove listeners[i]
+ for (int j = i + 1; j < listeners.length; j++) {
+ if (listeners[i].getClass().getName().equals(listeners[j].getClass().getName())){
+ removePhaseListener(listeners[i]);
+ break; //go to the next i
+ }
+ }
+ }
+ redundantPhaseListenersRemoved = true;
+ }
AsyncResponse async = AsyncResponse.getInstance();
if (AsyncResponse.isAjaxRequest()) {
async.installOnOffResponse(context);
@@ -139,9 +153,7 @@
public void removePhaseListener(PhaseListener phaseListener) {
- if (!parentHasListenerOfClass(phaseListener)) {
- parent.removePhaseListener(phaseListener);
- }
+ parent.removePhaseListener(phaseListener);
}
public void addPhaseListener(PhaseListener phaseListener) {