Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
847 views
in Technique[技术] by (71.8m points)

jsf 2.2 - java.lang.IllegalStateException: java.lang.InstantiationException while implementing a custom tag handler in JSF

Given the following tag handler class.

public final class ViewParamValidationFailed extends TagHandler implements ComponentSystemEventListener
{
    private final String redirect;

    public ViewParamValidationFailed(TagConfig config) {
        super(config);
        redirect = getRequiredAttribute("redirect").getValue();
    }

    @Override
    public void apply(FaceletContext context, UIComponent parent) throws IOException {
        if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
            ((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this);
        }
    }

    @Override
    public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.isValidationFailed()) {
            try {
                ExternalContext externalContext = context.getExternalContext();
                externalContext.redirect(externalContext.getRequestContextPath() + redirect);
            }
            catch (IOException e) {
                throw new AbortProcessingException(e);
            }
        }
    }
}

The tag handler is just meant to redirect to a page, when conversion fails.

This is used on an XHTML pages as follows.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:my="http://example.com/ui"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <h:head>
        <title>Test</title>
    </h:head>

    <h:body>
        <f:metadata>
            <f:viewParam name="id" required="true" value="#{testManagedBean.id}"/>
            <my:viewParamValidationFailed redirect="/public_resources/PageNotFound.jsf"/>
        </f:metadata>

        <h:form id="form" prependId="true">
            <p:commandButton value="Submit" actionListener="#{testManagedBean.submitAction}"/>
        </h:form>
    </h:body>
</html>

we can have an optional converter attribute with <f:viewParam> just like converter="javax.faces.Long"

In reality, there is a template in which <f:metadata> is enclosed within <ui:define name="metaData">.

The associated JSF managed bean:

@ManagedBean
@RequestScoped
public final class TestManagedBean
{
    private Long id; // Getter and setter.

    public TestManagedBean() {}

    public void submitAction() {
        System.out.println("submitAction() called.");
    }
}

When the given <p:commandButton> is pressed, the following exception is thrown.

SEVERE:   java.lang.IllegalStateException: java.lang.InstantiationException: tags.ViewParamValidationFailed
    at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:153)
    at javax.faces.component.UIComponent$ComponentSystemEventListenerAdapter.restoreState(UIComponent.java:2633)
    at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:165)
    at javax.faces.component.UIComponentBase.restoreAttachedState(UIComponentBase.java:1793)
    at javax.faces.component.UIComponentBase.restoreSystemEventListeners(UIComponentBase.java:1911)
    at javax.faces.component.UIComponentBase.restoreState(UIComponentBase.java:1607)
    at javax.faces.component.UIViewRoot.restoreState(UIViewRoot.java:1771)
    at com.sun.faces.application.view.FaceletPartialStateManagementStrategy$2.visit(FaceletPartialStateManagementStrategy.java:380)
    at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:151)
    at javax.faces.component.UIComponent.visitTree(UIComponent.java:1690)
    at com.sun.faces.application.view.FaceletPartialStateManagementStrategy.restoreView(FaceletPartialStateManagementStrategy.java:367)
    at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:138)
    at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:123)
    at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:590)
    at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:150)
    at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:353)
    at org.omnifaces.viewhandler.RestorableViewHandler.restoreView(RestorableViewHandler.java:66)
    at javax.faces.application.ViewHandlerWrapper.restoreView(ViewHandlerWrapper.java:353)
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:197)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:121)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.InstantiationException: tags.ViewParamValidationFailed
    at java.lang.Class.newInstance0(Class.java:357)
    at java.lang.Class.newInstance(Class.java:325)
    at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:150)
    ... 54 more

This tag handler should only be associated with <f:viewParam> and should be skipped in its entirely, when a post back is made, for example.

Is there any solution?


Crucial :

In the conditional check inside the apply() method,

if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
    System.out.println("Inside if");
    ((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this);
}

When the given button is clicked, this condition is evaluated to false as obvious. Hence, there should not be any problem.

Surprisingly, the exception disappears, when the only line inside the if statement is removed like as follows.

if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
    //Debug statements only.
}

It doesn't throw the exception as mentioned above, when the given <p:commandButton> is pressed though the condition is evaluated to false, in this case.

Never saw this kind of situation :)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

By default, JSF serializes the view state (the component tree state) for any <h:form> in the view (as <input type="hidden" name="javax.faces.ViewState">). This is restored during the restore view phase of the postback request on that form. The JSF view state covers among others the component system event listeners of the components in the tree.

This line

((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this);

adds one to the UIViewRoot component and this then needs to be serialized.

You've 4 options:

  1. Let it implement Serializable.
  2. Let it implement Externalizable.
  3. Unsubscribe the listener when it's done with its job.
  4. Use SystemEventListener instead of ComponentSystemEventListener.

In this particular case, as long as you don't need to support the nesting of the tag inside the individual <f:viewParam>, but only in <f:metadata>, then option 4 should suffice. Replace the ComponentSystemEventListener interface by SystemEventListener, implement the isListenerForSource() method to return true for UIViewRoot only, finally use UIViewRoot#subscribeToViewEvent() instead to subscribe the listener.

public final class ViewParamValidationFailed extends TagHandler implements SystemEventListener {
    private final String redirect;

    public ViewParamValidationFailed(TagConfig config) {
        super(config);
        redirect = getRequiredAttribute("redirect").getValue();
    }

    @Override
    public void apply(FaceletContext context, UIComponent parent) throws IOException {
        if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
            ((UIViewRoot) parent).subscribeToViewEvent(PostValidateEvent.class, this);
        }
    }

    @Override
    public boolean isListenerForSource(Object source) {
        return source instanceof UIViewRoot;
    }

    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        FacesContext context = FacesContext.getCurrentInstance();

        if (context.isValidationFailed()) {
            try {
                ExternalContext externalContext = context.getExternalContext();
                externalContext.redirect(externalContext.getRequestContextPath() + redirect);
            }
            catch (IOException e) {
                throw new AbortProcessingException(e);
            }
        }
    }

}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...