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 :)
Generally, this method is used to indicate a method is called at an illegal or inappropriate time. Example: After starting a thread we are not allowed to restart the same thread once again otherwise we will get Runtime Exception saying IllegalStateException.
When the IllegalArgumentException is thrown, you must check the call stack in Java’s stack trace and locate the method that produced the wrong argument. The IllegalArgumentException is very useful and can be used to avoid situations where your application’s code would have to deal with unchecked input data.
In the above example if we call start () method only once on thread t then we will not get any java.lang.IllegalStateException because we are not calling the start () method after the starting of thread (i.e we are not calling the start () method at an illegal or an inappropriate time.) Writing code in comment?
This exception is rise explicitly by programmer or by the API developer to indicate that a method has been invoked at the wrong time. Generally, this method is used to indicate a method is called at an illegal or inappropriate time.
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:
Serializable
.Externalizable
.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);
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With