[seam-issues] [JBoss JIRA] (JBSEAM-4919) seam-ui example java.lang.IndexOutOfBoundsException in restoreState

Brian Leathem (JIRA) jira-events at lists.jboss.org
Thu May 10 02:38:19 EDT 2012


    [ https://issues.jboss.org/browse/JBSEAM-4919?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12691670#comment-12691670 ] 

Brian Leathem commented on JBSEAM-4919:
---------------------------------------

Setting a breakpoint at the code where the exception occurs:
{code:title="AttachedObjectListHolder#Line 162"}
} else {
    // assume 1:1 relation between existing attachedObjects and state
    for (int i = 0, len = attachedObjects.length; i < len; i++) {
        T l = this.attachedObjects.get(i);
        if (l instanceof StateHolder) {
            ((StateHolder) l).restoreState(context, attachedObjects[i]);
        }
    }
}
{code}

which is in the _restoreState_ method of the _AttachedObjectListHolder_ and is invoked by the _restoreState_ method of the _UIInput_ component.

The problem comes about, as we are violating the assumption noted in the comment of the above code.  Namely _attachedObjects_ is of length 2, and _this.attachedObjects_ is of length 1.  To understand why, it is important to realize the _attachedObjects_ is the stored state of the _UIInput_ component, and refers to two validators (_BeanValildator_ and _EqualityValidator_) where as _this.attachedObjects_ refers to the list of validators currently attached to the _UIInput_ component (and consists of only a _BeanValildator_).

This discrepancy arises because the _EqualityValidator_ is added to the _UIInput_ list of validators in the encode method of _EqualityValidatorRendererBase_, whereas the _restoreState_ method is called much earlier in the lifecycle than the encode method.

The curious part here is why it works on the first submit. The answer is that we follow a different path through the _AttachedObjectListHolder_ _saveState_ method on initial page request, vs. the first page submit - based on the value of the _AttachedObjectListHolder_ _initialState_ property.  See the code below:

{code:title="AttachedObjectListHolder#Line 110"}
if (initialState) {
    Object[] attachedObjects = new Object[this.attachedObjects.size()];
    boolean stateWritten = false;
    for (int i = 0, len = attachedObjects.length; i < len; i++) {
        T attachedObject = this.attachedObjects.get(i);
        if (attachedObject instanceof StateHolder) {
            StateHolder sh = (StateHolder) attachedObject;
            if (!sh.isTransient()) {
                attachedObjects[i] = sh.saveState(context);
            }
            if (attachedObjects[i] != null) {
                stateWritten = true;
            }
        }
    }
    return ((stateWritten) ? attachedObjects : null);
} else {
    Object[] attachedObjects = new Object[this.attachedObjects.size()];
    for (int i = 0, len = attachedObjects.length; i < len; i++) {
        attachedObjects[i] = UIComponentBase.saveAttachedState(context, this.attachedObjects.get(i));
    }
    return (attachedObjects);
}
{code}

During the initial request, _initialState_ is _false_, and the validator state is saved by delegating to the _UIComponentBase.saveAttachedState_ method using _StateHolderSaver_ objects.  After the first request, the _initialState_ is true, and we invoke the saveState method on the _EqualityValidator_ directly.  This then affects the path we take through the _restoreState_ method, where the error is ultimately thrown:

{code:title="AttachedObjectListHolder#Line 148"}
if (attachedObjects.length > 0 && attachedObjects[0] instanceof StateHolderSaver) {
    // overwrite the existing attachedObjects with those included
    // in the full state.
    if (this.attachedObjects != null) {
        this.attachedObjects.clear();
    } else {
        this.attachedObjects = new ArrayList<T>(2);
    }
    for (int i = 0, len = attachedObjects.length; i < len; i++) {
        T restored = (T) ((StateHolderSaver) attachedObjects[i]).restore(context);
        if (restored != null) {
            this.attachedObjects.add(restored);
        }
    }
} else {
    // assume 1:1 relation between existing attachedObjects and state
    for (int i = 0, len = attachedObjects.length; i < len; i++) {
        T l = this.attachedObjects.get(i);
        if (l instanceof StateHolder) {
            ((StateHolder) l).restoreState(context, attachedObjects[i]);
        }
    }
}
{code}

In the first pass through the _restoreState_ method, where our state is stored in _StateHolderSaver_ objects by the _UIComponentBase_ delegation, we "overwrite the existing attachedObjects with those included in the full state" (from the embedded comment).  Whereas the 2nd time through, the validator saved its state itself and we go through the 2nd path, where the exception is thrown - since we haven't yet registered the _EqualityValidator_ with the _UIInput_ component.

Having discovered this, I thought I could work around the problem by having the _EqualityValidator_ save/restore state methods also delegate to _UIComponentBase_ for state saving, as in:
{code:title="Attempt at delegating state saving to UIComponentBase"}
public void restoreState(FacesContext context, Object stateHolder) {
    if (stateHolder != null) {
        Object state = UIComponentBase.restoreAttachedState(context, stateHolder);
        Object[] fields = (Object[]) state;
        forId = (String) fields[0];
        message = (String) fields[1];
        messageId = (String) fields[2];
        operator = ValidOperation.valueOf((String) fields[3]);
    }
}

public Object saveState(FacesContext context) {
    Object[] state = new Object[4];
    state[0] = forId;
    state[1] = message;
    state[2] = messageId;
    state[3] = operator.toString();
    Object stateHolder = UIComponentBase.saveAttachedState(context, state);
    return stateHolder;
}
{code}

But this did not resolve the problem, as the _BeanValidator_ is in the 0th position of the list, and since it does not perform a similar delegation in it's save/restore state methods, we still follow the same paths through the above code.

*TL;DR:*
A proper fix to this issue will involve finding a different means of registering the _EqualityValidator_ with the _UIInput_ component.  This should be done at the time the component tree is built, rather than we the component tree is encoded.
                
> seam-ui example java.lang.IndexOutOfBoundsException in restoreState
> -------------------------------------------------------------------
>
>                 Key: JBSEAM-4919
>                 URL: https://issues.jboss.org/browse/JBSEAM-4919
>             Project: Seam 2
>          Issue Type: Bug
>          Components: Core
>    Affects Versions: 2.3.0.BETA1
>         Environment: AS 7.1.0.Final, AS 7.1.1.Final
>            Reporter: Marek Schmidt
>            Assignee: Marek Novotny
>            Priority: Blocker
>              Labels: jsf2, jsf21, ui
>             Fix For: 2.3.0.BETA2
>
>
> In the seam-ui example, a debug page is shown 
> {noformat}
> Exception during request processing:
> Caused by javax.servlet.ServletException with message: "Index: 1, Size: 1"
> javax.faces.webapp.FacesServlet.service(FacesServlet.java:606)
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
> org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
> org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50)
> org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
> org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
> org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
> org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
> org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
> java.lang.Thread.run(Thread.java:662)
> Caused by java.lang.IndexOutOfBoundsException with message: "Index: 1, Size: 1"
> java.util.ArrayList.RangeCheck(ArrayList.java:547)
> java.util.ArrayList.get(ArrayList.java:322)
> javax.faces.component.AttachedObjectListHolder.restoreState(AttachedObjectListHolder.java:165)
> javax.faces.component.UIInput.restoreState(UIInput.java:1411)
> com.sun.faces.application.view.StateManagementStrategyImpl$2.visit(StateManagementStrategyImpl.java:264)
> com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
> javax.faces.component.UIComponent.visitTree(UIComponent.java:1612)
> javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
> javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
> javax.faces.component.UIForm.visitTree(UIForm.java:371)
> javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
> javax.faces.component.UIComponent.visitTree(UIComponent.java:1623)
> com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:251)
> com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:188)
> org.jboss.seam.jsf.SeamStateManager.restoreView(SeamStateManager.java:76)
> com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
> com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:453)
> com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:142)
> org.jboss.seam.jsf.SeamViewHandler.restoreView(SeamViewHandler.java:94)
> com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:192)
> com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
> com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116)
> com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
> javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
> org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53)
> org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
> org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280)
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
> org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50)
> org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153)
> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
> org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
> org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
> org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
> org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
> java.lang.Thread.run(Thread.java:662)
> {noformat}

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.jboss.org/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the seam-issues mailing list