[ https://issues.apache.org/jira/browse/MYFACES-1820?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12792419#action_12792419 ]
Simon Kitching commented on MYFACES-1820: ----------------------------------------- Yes, Leonardo's posting shows the latest code. I'm not sure Leonardo's suggested change is needed though. The whole point of the patch I applied is that we can have this situation: custom FacesContext "decorator" object #1 (eg from Orchestra) --> custom FacesContext "decorator object #2" (eg from PrettyFaces) [1] --> the "base" implementation (eg the standard MyFaces FacesContextImpl object) There are two situations when a decorator class wants to do some of its own logic *then* delegate to the next instance in the chain: (a) when it wants to do something *in addition to the standard logic* (b) when the class wants to be useable with versions of JSF earlier than the one it was compiled with; in this case it needs to provide "stubs" for new methods, which just delegate to the wrapped instance. The custom subclasses of FacesContext are supposed to just call the corresponding method on the super-class (FacesContext), and let that delegate to the wrapped instance. However in the original code, what was delegated to was whatever object setCurrentInstance() had been called on. Obviously, when the wrapper has set itself as the "current instance" a loop occurs [2]. Unfortunately, JSF makes finding the "wrapped" instance very difficult. The current code (this _firstInstance stuff) just returns the "base" implementation every time, which means it can skip objects in the middle of the chain. But it is better than nothing. Leonardo's suggested check for if (ctx == this) should not be necessary as far as I can see, because "ctx" will always be the "base" implementation, and that should never call methods on the superclass; the "base" implementation is required to override the getELContext method in the FacesContext class with a proper implementation. [1] I'm not 100% sure that PrettyFaces customises FacesContext, but certainly some libraries out there do. Sorry, I've forgotten exactly which ones definitely do it.. [2] I guess that the JSF designers had assumed that the ability to customise the FacesContextFactory would be used only to completely replace a FacesContext implementation, rather than to write a decorator. But FacesContext decorators are very useful - in fact, critical in some cases. And several libraries out there already use them. > Infinite loop can occur when custom FacesContext subclass compiled against > JSF1.1 but used with JSF1.2 > ------------------------------------------------------------------------------------------------------ > > Key: MYFACES-1820 > URL: https://issues.apache.org/jira/browse/MYFACES-1820 > Project: MyFaces Core > Issue Type: Bug > Components: JSR-252 > Affects Versions: 1.2.2 > Reporter: Simon Kitching > Attachments: FacesContext.patch.txt, patch-1820.txt > > > The problem is method FacesContext.getELContext. JSF1.2 added a method to > this base class that was not there in JSF1.1. This makes life difficult for > existing JSF1.1 code that already subclasses that class. > A default concrete implementation needs to exist, in order not to break > existing JSF1.1 code, but (a) the current one gets it wrong, and (b) defining > a correct one is difficult (impossible?) > (1) Stan Silvert initially defined this method like this: > // The following concrete method was added for JSF 1.2. > // It supplies a default > // implementation that throws UnsupportedOperationException. > // This allows old FacesContext implementations to still work. > public ELContext getELContext() { > throw new UnsupportedOperationException(); > } > (2) Dennis Byrne changed it to its current form: > public ELContext getELContext() { > FacesContext ctx = getCurrentInstance(); > if (ctx == null) > throw new NullPointerException(FacesContext.class.getName()); > ELContext elctx = ctx.getELContext(); > if (elctx == null) > throw new UnsupportedOperationException(); > return elctx; > } > However (2) assumes that custom subclasses never set themselves as the > current instance, instead only ever *delegating* to the "real" instance. > If someone's custom subclass of FacesContext ever calls > setCurrentInstance(this), then an infinite loop will occur here. > And in fact, this is just what we get: > java.lang.StackOverflowError > at java.lang.ThreadLocal$ThreadLocalMap.getEntry(ThreadLocal.java:357) > at java.lang.ThreadLocal$ThreadLocalMap.access$000(ThreadLocal.java:242) > at java.lang.ThreadLocal.get(ThreadLocal.java:127) > at > javax.faces.context.FacesContext.getCurrentInstance(FacesContext.java:98) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:35) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) > at javax.faces.context.FacesContext.getELContext(FacesContext.java:40) -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.