Programming tips for various technologies.

martedì 28 ottobre 2008

MyFaces: handling the ViewExpiredException

I am developing some Java Server Faces' technologies related applications from about one year. I have studied for long time this very interesting web framework and i must say i really like it. However even JSF can be very problematic while trying to handle complex things.

The main drawback i had to fight against is the documentation. JSF's specifications had many revisions. We now are at the 1.2 specifications version and browsing the web for fresh documentation is very difficoult. You can find over the network a lot of useful articles and pieces of code but you can also find a lot of old useless things and outdated news.
Usually people tend to help you (if you use Mailing Lists) but sometimes they don't.

Anyway one of the problems I wanted to solve is the ViewExpiredException handling. I studied this problem a lot and tried to document myself the best i could. I also found out some useful articles and blogs related to this problem.

I post these link here for completeness :
  1. JSF dev archive
  2. Sun's forum
  3. Mert Caliskan's weblog (opened my eyes)
  4. Myfaces wiki

Now to My solution.

I am using Apache MyFaces cause it is heavily developed and it has a lot of useful add-on libraries such as Tomahawk, Trinidad and others. Even if Mert Caliskan's articles was really almost the solution it does not work with Myfaces. And honestly i was not able to implement it under Sun's JSF 1.2 as well.

The main problem here is that the JSF 1.2 implementation have somehow a bug (actually i did not understand how come) and the simple solution to this problem does not work at all even on Myfaces and Sun's JSF 1.2.


Simple solution : put into web.xml the following code

<
error-page>
<
exception-type>javax.faces.application.ViewExpiredException< /exception-type>
<
location>/error.jsp< /location>
< /error-page>


The real working solution is a bit tricky. It is based to the fact that Sun's JSF main Servlet does have some code that handles exceptions. So the proposed solution was to use the Servlet in some way. Now JSF's Servlet cannot be subclassed so we must use the Delegation as perfectly described by Mert Caliskan.
I could not anyway figure out how to configure web.xml to let the application deploy without problems (and this is still an open problem to me).

So i decided to try with MyFaces (it works with 1.2.22 and later, I did not test with earlier versions).

Here the job is even simplier cause we have already a delegated Servlet that is MyFacesServlet.java. So i only needed to subclass it. Here is the code:

public class MyFacesServletWrapper extends MyFacesServlet
{

private static final String SYSTEM_ERROR_URI = "/systemerror.jsf";
private static final String VIEWEX_ERROR_URI = "/viewexpiredexception.jsf";

@Override
public void service(ServletRequest request, ServletResponse response) throws IOException,ServletException
{
try {
super.service(request, response);
}
catch(ServletException e) {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
if (e.getCause().getClass().equals(javax.faces.application.ViewExpiredException.class))
{
// here there is the ViewExpiredException
res.sendRedirect(req.getContextPath() +
VIEWEX_ERROR_URI);
}
else {
// any other exception
req.getSession().invalidate();
res.sendRedirect(req.getContextPath() +
SYSTEM_ERROR_URI);
}
}
}
}

However we still haven't finished the job. We must modify in the correct way the web.xml in order to really catch those exceptions. Finally adding the following lines does the job:


< context-param>
< description>Use this to suppress Facelets error page< /description>
< param-name>org.apache.myfaces.ERROR_HANDLING< /param-name>
<
param-value>false< /param-value>
< /
context-param>

< servlet>
< servlet-name>Use this to suppress Facelets error page< /servlet-name>
< servlet-class>MyFacesServletWrapper< /servlet-class>
<
load-on-startup>1< /load-on-startup>
< /
servlet>


That worked to me. Hope you won't have any problem in running this trick.
Please comment if you have any problems.

10 commenti:

laurentw ha detto...

Hello,

The "simple" solution will work if you forward the exception to a non-faces page : /index.html

It permit to start a new session.

On the index.html, you only put :

meta http-equiv="Refresh" content="0; URL=login.faces"


and in web.xml, you configure the welcome page : index.html

Vicente ha detto...

Nice solution, but it is not working for me in case the exception is raised while rendering the page. That is, when FacesServlet is calling to "_lifecycle.render()" and an exception occurs (for ex. a "ServletException"), the exception is catched in the block, but the redirection fails because the response has already been comitted.

The same happens with the "simple" solution, it gives an "IllegalStateException" because the response has already been committed...

Any ideas??

Thanks in advance,
Beatriz

Patton ha detto...

Ehm i do not get the point.
If you get a rendering axception usually means that you wrote a wrong facelets or JSP code.
I do not think that is related to a run-time exception like the ViewHandlerException.

Anyway do you have a test case or code to share that shows the problem?

Thanks in advance.

Tobias M. ha detto...

Good work, Patton! And thanks for the additional advice concerning the "simple solution", laurentw! All that helped me a lot.

Tosta

jupp ha detto...

Hello,

thanx a lot of for the programming tip and useful comments.

I used the described way with the MyFacesServlet.java and the redirect to the ErrorExpired-View. At the moment I have 2 problems:

1) The redirect doesn't always work. Sometimes in Firefox 3.5.6 and IE7 I can see only the HTML-Page with the redirect-command in the HEADER, but the redirect is not done.

2) We use JAAS from the Box (JBoss 5.1) and therefore before redirecting to the ErrorExpired-View there is the login-Page shown. So the user has to type in his credentials to get as response the ErrorExpired-View. So as a result the user has to login one more time to access the application.

Any suggestions to solve the problem in 2) would be very appreciated.

Josef

Patton ha detto...

Hi Jupp, sorry for the late answer.

About your first problem is surely related to some misconfiguration of your server. The page is not always rendered in the correct way.
This should not be related to nay browser bug.
Anyw you should check the sever side.


About the second problem i am afraid i will not be able to help you. I never used JAAS.
Anyway what comes to my mind is that pheraps is there a sort of filter that requires user authentication even to display the error page.
You should check how to tell the application server to redirect the page without authenticate the user.

Anyway you should provide more information about yoyr problem.
It is very difficult to guess the problem with such little information.

See you.

Giovanni ha detto...

Gr8 Work!!!!!!!! I use Trinidad and I spent 2 days on google without success... Many thanks!!!

gerardo ha detto...

where goes the class MyFacesServletWrapper

thanks a lot in advance

Patton ha detto...

Sorry for the late answer.
Well you can put the MyFacesServletWrapper inside the /src folder of your project.
Let's say /src/wrapper.
After that you must bind it with the web.xml in this way :

< servlet>
< servlet-name>Myfaceswrapper< /servlet-name>
< servlet-class>/src/wrapper/ MyFacesServletWrapper< /servlet-class>
< load-on-startup>1< /load-on-startup>
< /servlet>

Axel Osorio ha detto...

Hello. Thank you Very very much. In My case the simple solution works for me.
I only put in my web.xml this lines:

javax.faces.application.ViewExpiredException
/punica/pages/application/security/LoginPage.jsf


I'm working with JSF 1.2. And may be that works for me because the mapping for JSF.



Faces Servlet
*.jsf

Lettori fissi