ajax ppr redirect on error

Components, Ajax Framework, Utilities and More.
User avatar
michiel
Posts: 240
Joined: 07 Jun 2010, 09:12
Location: Belgium

07 Jun 2010, 10:26

I 've written a single page app with only navigation from login page through app and vice versa.
The only problem I faced is the view expired exception (session timeout).
When this happens I want to redirect to the login page (if possible with a message for the user)
but I can't get it implemented.

I've tried to extend the FacesServlet, to add a SessionListener, to use the onerror of <p:ajaxStatus>, ...

Any help is appreciated very much
;)
JSF-2.0, mojarra-2.0.2-FCS and PrimeFaces-2.1 on GlassFish v3.0.1 (build 22)

User avatar
Oleg
Expert Member
Posts: 3802
Joined: 02 Oct 2009, 09:41
Location: Germany, Black Forest

07 Jun 2010, 10:40

michiel wrote:I 've written a single page app with only navigation from login page through app and vice versa.
The only problem I faced is the view expired exception (session timeout).
When this happens I want to redirect to the login page (if possible with a message for the user)
but I can't get it implemented.

I've tried to extend the FacesServlet, to add a SessionListener, to use the onerror of <p:ajaxStatus>, ...

Any help is appreciated very much
;)
Do you use JSF2? I have a solution for JSF2 with a special phase listener and default error handler.
PrimeFaces Cookbook (2. edition): http://ova2.github.io/primefaces-cookbook/ Learning Angular UI Development with PrimeNG: https://github.com/ova2/angular-develop ... th-primeng Blog: https://medium.com/@OlegVaraksin

User avatar
optimus.prime
Prime
Posts: 17420
Joined: 05 Jan 2009, 00:21
Location: Cybertron
Contact:

07 Jun 2010, 10:45

Check out Ed's blog to handle viewExpiredExceptions;

http://weblogs.java.net/blog/edburns/ar ... ption-jsf2

User avatar
michiel
Posts: 240
Joined: 07 Jun 2010, 09:12
Location: Belgium

07 Jun 2010, 11:18

I do use JSF-2.0, mojarra-2.0.2-FCS and PrimeFaces-2.0.3-SNAPSHOT on GlassFishv3.

I'll try to http://weblogs.java.net/blog/edburns/ar ... ption-jsf2

Thanks
JSF-2.0, mojarra-2.0.2-FCS and PrimeFaces-2.1 on GlassFish v3.0.1 (build 22)

User avatar
Oleg
Expert Member
Posts: 3802
Joined: 02 Oct 2009, 09:41
Location: Germany, Black Forest

07 Jun 2010, 11:36

Hello,

For Ajax redirects you needs a little bit more code than in Ed's blog. I'm going to post here my solution (just three centric classes to show the idea). I hope it will help you.

DefaultExceptionHandlerFactory

Code: Select all

/**
 * This is a factory class that creates (if needed) and returns a new {@link DefaultExceptionHandler} instance.
 *
 * <p>Using this functionality requires the following section in the faces-config.xml:</p>
 *
 * <pre>
   <factory>
       <exception-handler-factory>ip.client.jsftoolkit.exceptions.DefaultExceptionHandlerFactory</exception-handler-factory>
   </factory>
 * </pre>
 */
public class DefaultExceptionHandlerFactory extends ExceptionHandlerFactory
{
	//~ Instance fields --------------------------------------------------------

	private ExceptionHandlerFactory parent;

	//~ Constructors -----------------------------------------------------------

	/**
	 * Creates a new DefaultExceptionHandlerFactory object.
	 *
	 * @param parent parent instance of {@link ExceptionHandlerFactory}
	 */
	public DefaultExceptionHandlerFactory(ExceptionHandlerFactory parent)
	{
		this.parent = parent;
	}

	//~ Methods ----------------------------------------------------------------

	/**
	 * @see javax.faces.context.ExceptionHandlerFactory#getExceptionHandler()
	 */
	@Override
	public ExceptionHandler getExceptionHandler()
	{
		ExceptionHandler eh = parent.getExceptionHandler();
		eh = new DefaultExceptionHandler(eh);

		return eh;
	}
}
DefaultExceptionHandler

Code: Select all

/**
 * Default implementation of exception handler to catch unchecked / unexpected exceptions in order to proper display.
 * The method {@link #handleUnexpected(FacesContext, Throwable)} can be overwritten in derived class in order to
 * customization. Handling of various unexpected exceptions can be done there. Example of using:
 *
 * <pre>
    protected String handleUnexpected(FacesContext facesContext, final Throwable t)
    {
        if (t instanceof IllegalStateException) {
            // some special handling here
            ...
            return "key.exception.IllegalStateException";
        } else {
            return super.handleUnexpected(facesContext, t);
        }
    }
   </pre>
 */
public class DefaultExceptionHandler extends ExceptionHandlerWrapper
{
	//~ Static fields/initializers ---------------------------------------------

	/** logger */
	private static final Log LOG = LogFactory.getLog(DefaultExceptionHandler.class);

	/** key for session scoped message detail */
	public static final String MESSAGE_DETAIL_KEY = "ip.client.jsftoolkit.messageDetail";

	//~ Instance fields --------------------------------------------------------

	private ExceptionHandler wrapped;

	//~ Constructors -----------------------------------------------------------

	/**
	 * Creates a new DefaultExceptionHandler object.
	 *
	 * @param wrapped wrapped exception handler instance
	 */
	public DefaultExceptionHandler(ExceptionHandler wrapped)
	{
		this.wrapped = wrapped;
	}

	//~ Methods ----------------------------------------------------------------

	/**
	 * @see javax.faces.context.ExceptionHandlerWrapper#getWrapped()
	 */
	@Override
	public ExceptionHandler getWrapped()
	{
		return this.wrapped;
	}

	@Override
	public void handle() throws FacesException
	{
		for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
			ExceptionQueuedEvent event = i.next();
			ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();

			String redirectPage = null;
			FacesContext fc = FacesContext.getCurrentInstance();
			Throwable t = context.getException();

			try {
				if (t instanceof AbortProcessingException) {
					// about AbortProcessingException see JSF 2 spec.
					LOG.error("An unexpected exception has occurred by event listener(s)", t);
					redirectPage = "/views/error.jsf?statusCode=jsftoolkit.exception.UncheckedException";
					fc.getExternalContext().getSessionMap()
					  .put(DefaultExceptionHandler.MESSAGE_DETAIL_KEY, t.getLocalizedMessage());
				} else if (t instanceof ViewExpiredException) {
					if (LOG.isDebugEnabled()) {
						LOG.debug("View '" + ((ViewExpiredException) t).getViewId() + "' is expired", t);
					}

					ApplicationConfiguration appConfiguration =
					    (ApplicationConfiguration) FacesAccessor.accessManagedBean(
					        fc, ApplicationConfiguration.BEAN_NAME_APPLICATION_CONFIGURATION);
					HttpSession session = (HttpSession) fc.getExternalContext().getSession(false);
					if (session != null) {
						// should not happen
						session.invalidate();
					}

					if (appConfiguration.getBoolean(ConfigKeys.KEY_LOGOFF_2_LOGOUT_PAGE, false)) {
						// redirect to the specified logout page
						redirectPage = "/views/logout.jsf";
					} else {
						// redirect to the login page
						redirectPage = "";
					}
				} else if (t instanceof ServiceNotAvailableException) {
					LOG.error("'" + ((ServiceNotAvailableException) t).getServiceName() + "' is not available", t);
					redirectPage = "/views/error.jsf?statusCode=jsftoolkit.exception.ServiceNotAvailableException";
				} else {
					// custom handling of unexpected exceptions can be done in the method handleUnexpected
					String messageKey = handleUnexpected(fc, t);
					redirectPage = "/views/error.jsf?statusCode=" + messageKey;
					fc.getExternalContext().getSessionMap()
					  .put(DefaultExceptionHandler.MESSAGE_DETAIL_KEY, t.getLocalizedMessage());
				}
			} finally {
				i.remove();
			}

			SecurityPhaseListener spl = new SecurityPhaseListener();
			spl.doRedirect(fc, redirectPage);

			break;
		}
	}

	/**
	 * This method can be overwritten in derived classe in order to customization. Handling of various unexpected
	 * exceptions can be done here.
	 *
	 * @param  facesContext current faces context {@link FacesContext}
	 * @param  t            Throwable exception
	 * @return String key for error message
	 */
	protected String handleUnexpected(FacesContext facesContext, final Throwable t)
	{
		LOG.error("An unexpected internal error has occurred", t);

		return "jsftoolkit.exception.UncheckedException";
	}
}
SecurityPhaseListener with redirect functionality (unfortunately with a little workaround for PrimeFaces, Cagatay? :-))

Code: Select all

/**
 * Phase listener for the Restore View Phase which manages display of login page.
 */
public class SecurityPhaseListener implements PhaseListener
{
	//~ Static fields/initializers ---------------------------------------------

	/** logger */
	private static final Log LOG = LogFactory.getLog(SecurityPhaseListener.class);

	//~ Methods ----------------------------------------------------------------

	public void afterPhase(PhaseEvent event)
	{
		;
	}

	public void beforePhase(PhaseEvent event)
	{
		FacesContext fc = event.getFacesContext();

		String loginPage = (String) fc.getExternalContext().getRequestMap().get(Authenticator.SHOW_LOGIN_ATTRIBUTE);
		if (StringUtils.isNotBlank(loginPage)) {
			doRedirect(fc, loginPage);
		}
	}

	public PhaseId getPhaseId()
	{
		return PhaseId.RESTORE_VIEW;
	}

	/**
	 * Does a regular or ajax redirect.
	 *
	 * @param  fc        current faces context
	 * @param  loginPage context relative login page URL
	 * @throws FacesException if redirect failed
	 */
	public void doRedirect(FacesContext fc, String loginPage) throws FacesException
	{
		ExternalContext ec = fc.getExternalContext();

		try {
			// workaround for PrimeFaces
			new RequestContextImpl(ec);
			if (ec.getRequestParameterMap().containsKey(Constants.PARTIAL_PROCESS_PARAM)
			    && !ec.getRequestParameterMap().get(Constants.PARTIAL_PROCESS_PARAM).equals("@all")) {
				fc.setViewRoot(new PartialViewRoot(new UIViewRoot()));
			}

			// fix for renderer kit (Mojarra's and PrimeFaces's ajax redirect)
			if ((RequestContext.getCurrentInstance().isAjaxRequest()
			     || fc.getPartialViewContext().isPartialRequest())
			    && fc.getResponseWriter() == null
			    && fc.getRenderKit() == null) {
				ServletResponse response = (ServletResponse) ec.getResponse();
				ServletRequest request = (ServletRequest) ec.getRequest();
				response.setCharacterEncoding(request.getCharacterEncoding());

				RenderKitFactory factory =
				    (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);

				//RenderKit renderKit = factory.getRenderKit(fc, RenderKitFactory.HTML_BASIC_RENDER_KIT);
				RenderKit renderKit =
				    factory.getRenderKit(fc, fc.getApplication().getViewHandler().calculateRenderKitId(fc));

				ResponseWriter responseWriter =
				    renderKit.createResponseWriter(response.getWriter(), null, request.getCharacterEncoding());
				fc.setResponseWriter(responseWriter);
			}

			ec.redirect(ec.getRequestContextPath() + (loginPage != null ? loginPage : ""));
		} catch (IOException e) {
			LOG.error("Redirect to the specified page '" + loginPage + "' failed");
			throw new FacesException(e);
		}
	}
}
Configuration

Code: Select all

<?xml version="1.0"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
		version="2.0">

	<absolute-ordering>
		<others />
		<name>jsftoolkit</name>
	</absolute-ordering>
	<factory>
		<exception-handler-factory>
			ip.client.jsftoolkit.commons.DefaultExceptionHandlerFactory
		</exception-handler-factory>
	</factory>
	<lifecycle>
		<phase-listener>
			ip.client.jsftoolkit.commons.SecurityPhaseListener
		</phase-listener>
	</lifecycle>
        .....

</faces-config>
PrimeFaces Cookbook (2. edition): http://ova2.github.io/primefaces-cookbook/ Learning Angular UI Development with PrimeNG: https://github.com/ova2/angular-develop ... th-primeng Blog: https://medium.com/@OlegVaraksin

User avatar
michiel
Posts: 240
Joined: 07 Jun 2010, 09:12
Location: Belgium

07 Jun 2010, 11:42

okay,

first of all thanks for the quick responses.
I let you know of my progress
JSF-2.0, mojarra-2.0.2-FCS and PrimeFaces-2.1 on GlassFish v3.0.1 (build 22)

User avatar
michiel
Posts: 240
Joined: 07 Jun 2010, 09:12
Location: Belgium

08 Jun 2010, 09:26

with your help I finally figured it out.
I took the the solution from Ed Burns blog as base and fixed it with Oleg's response.

Here is the code.

ViewExpiredExceptionExceptionHandlerFactory

Code: Select all

public class ViewExpiredExceptionExceptionHandlerFactory extends ExceptionHandlerFactory {
    private ExceptionHandlerFactory parent;
    public ViewExpiredExceptionExceptionHandlerFactory(ExceptionHandlerFactory parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        ExceptionHandler result = parent.getExceptionHandler();
        result = new ViewExpiredExceptionExceptionHandler(result);
        return result;
    }
}
ViewExpiredExceptionExceptionHandler

Code: Select all

public class ViewExpiredExceptionExceptionHandler extends ExceptionHandlerWrapper {
    private ExceptionHandler wrapped;

    public ViewExpiredExceptionExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public void handle() throws FacesException {
        String redirectPage = null;
        for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
            Throwable t = context.getException();
            if (t instanceof ViewExpiredException) {
                ViewExpiredException vee = (ViewExpiredException) t;
                FacesContext fc = FacesContext.getCurrentInstance();
                HttpSession session = (HttpSession) fc.getExternalContext().getSession(true);
                try {
                    // Push some useful stuff to the request scope for use in the page
                    session.setAttribute("currentViewId", vee.getViewId());
                    System.out.println("currentViewId to put = " + vee.getViewId());
                    redirectPage = "/viewExpired.caw";
                } finally {
                    i.remove();
                }
                doRedirect(fc, redirectPage);
            }
        }

        // At this point, the queue will not contain any ViewExpiredEvents.
        // Therefore, let the parent handle them.
        getWrapped().handle();
    }

    public void doRedirect(FacesContext fc, String redirectPage) throws FacesException{
        ExternalContext ec = fc.getExternalContext();

        try {
            // workaround for PrimeFaces
		      new RequestContextImpl(ec);
            if (ec.getRequestParameterMap().containsKey(Constants.PARTIAL_PROCESS_PARAM)
                    && !ec.getRequestParameterMap().get(Constants.PARTIAL_PROCESS_PARAM).equals("@all")) {
                fc.setViewRoot(new PartialViewRoot(new UIViewRoot()));
            }

            // fix for renderer kit (Mojarra's and PrimeFaces's ajax redirect)
            if ((RequestContext.getCurrentInstance().isAjaxRequest()
                    || fc.getPartialViewContext().isPartialRequest())
                    && fc.getResponseWriter() == null
                    && fc.getRenderKit() == null) {
                ServletResponse response = (ServletResponse) ec.getResponse();
                ServletRequest request = (ServletRequest) ec.getRequest();
                response.setCharacterEncoding(request.getCharacterEncoding());

                RenderKitFactory factory =
                        (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
                RenderKit renderKit =
                        factory.getRenderKit(fc, fc.getApplication().getViewHandler().calculateRenderKitId(fc));
                ResponseWriter responseWriter =
                        renderKit.createResponseWriter(response.getWriter(), null, request.getCharacterEncoding());
                fc.setResponseWriter(responseWriter);
            }

            ec.redirect(ec.getRequestContextPath() + (redirectPage != null ? redirectPage : ""));
        } catch (IOException e) {
            System.out.println("Redirect to the specified page '" + redirectPage + "' failed");
            throw new FacesException(e);
        }
    }
}
faces-config.xml

Code: Select all

<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
              version="2.0">

    <!-- NAVIGATION RULES -->

    <!-- ViewExpiration Handler -->
    <factory>
        <exception-handler-factory>be.oac.caw.web.utils.ViewExpiredExceptionExceptionHandlerFactory</exception-handler-factory>
    </factory>

</faces-config>
Hope this might help someone else.

Thanks for al the help

User avatar
optimus.prime
Prime
Posts: 17420
Joined: 05 Jan 2009, 00:21
Location: Cybertron
Contact:

09 Jun 2010, 11:31

Thanks for sharing your solution with other users Michiel.

User avatar
bumble.bee
Posts: 723
Joined: 29 Sep 2010, 21:39
Location: United States

24 Nov 2010, 15:31

I'm not seeing any problems with the PrimeFaces AJAX redirect in PrimeFaces 2.2. I assume this issue was resolved and the workaround is no longer needed?

The workaround no longer would compile in its current form because the RequestContextImpl appears to have been replaced with DefaultRequestContext among other changes.

sideswipe
Posts: 5
Joined: 28 Sep 2010, 21:08

26 Nov 2010, 15:19

i have the same problem but using
jsf 1.2 (mojarra)
jboss seam 2.2
primefaces 1.2 snapshot
tomcat 6

i want to do a page-redirect after session timeout (@topic -> the observed error is: ViewExpiredException). so i installed a sessionlistener + sessiontimeoutfilter. next step is to send a redirect like:

Code: Select all

...
httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/home.seam");
...
to send the user to the default loginpage after timeout.

everything is triggered well and works, except i use ajax.
i got a <p:commandButton ...> for example. if it is triggered after sessiontimeout the code above is executed, but since the button got ajax support, there is no redirect made.
if i explicit say <p:commandButton ajax="false" ...> then the redirect works (but.. ajax would be nice to have ;) )


in the filter i have no access to the FacesContext so i cant go the way described for jsf 2.0
i also can't use the <exception-handler-factory> (not available in jsf 1.2)



so... is there a way to use the benefits of ajax support + get propperly redirected using jsf 1.2?

(i found a "hack" here.. http://stackoverflow.com/questions/1990 ... -ajax-call but (still? i try to look into that deeper) don't know where to apply it ;) )

thanks in advance

Post Reply
  • Information
  • Who is online

    Users browsing this forum: Bing [Bot] and 6 guests