JSF ExceptionHandler - Response already committed in some cases

209 Views Asked by At

We have implemented a custom ExceptionHandler that attempts to intercept any NonexistentConversationException that are thrown if a user requests a URL with an old cid in it (eg. bookmarked link etc.). The handler simply removed the cid from the URL and performs a redirect to the updated URL.

This solution seems to work in most cases, we have a number of different user 'roles' within the App each with their own login areas and for one of these user types it seems that fc.getExternalContext().isResponseCommitted() is always true when the ExceptionHandler fires which means we are unable to perform the redirect. All other user types it works ok.

I am not sure exactly what the difference is with this user type for this to happen, I am guessing some CDI bean we using setup differently

Is there a way to unsure the ExceptionHandler kicks in earlier before the response is committed?

Below is the handler...

public class ConversationExpiredExceptionHandler
        extends ExceptionHandlerWrapper {

    final static Logger log = LoggerFactory.getLogger(ConversationExpiredExceptionHandler.class);
    
    private ExceptionHandler wrapped;
    
    private static String CONVERSATION_PARAM = "cid";
    
    public ConversationExpiredExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @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();
            Throwable t = context.getException();

            if (t instanceof javax.enterprise.context.NonexistentConversationException 
                || t instanceof org.jboss.weld.contexts.NonexistentConversationException
                || t.getCause() instanceof org.jboss.weld.contexts.NonexistentConversationException /* can be wrapped in FacesException */) {

                final FacesContext fc = FacesContext.getCurrentInstance();
                
                try {
                    if (!fc.getExternalContext().isResponseCommitted()) {
                        if(Faces.getRequestQueryStringMap().containsKey(CONVERSATION_PARAM)) {
                            String requestedUrl = Faces.getRequestURLWithQueryString();
                            String updatedUrl = this.removeQueryParameter(requestedUrl,CONVERSATION_PARAM);
                            log.debug("No conversation active for {}, redirecting to {}",requestedUrl,updatedUrl);
                            fc.getExternalContext().redirect(updatedUrl);
                        }
                    }
                    fc.renderResponse();
                    break;
                } catch (Exception ex) {
                    throw new FacesException(ex);
                } finally {
                    i.remove();
                }
            }
        }
        getWrapped().handle();
    }
    
    private String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(url);
        List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
        for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
            NameValuePair queryParameter = queryParameterItr.next();
            if (queryParameter.getName().equals(parameterName)) {
                queryParameterItr.remove();
            }
        }
        uriBuilder.setParameters(queryParameters);
        return uriBuilder.build().toString();
    }
0

There are 0 best solutions below