/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.portletbridge.context.exception;
import java.util.Iterator;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.NavigationHandler;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.event.PhaseId;
import javax.servlet.ServletException;
import org.jboss.portletbridge.bridge.context.BridgeContext;
import org.jboss.portletbridge.bridge.factory.BridgeLoggerFactoryImpl;
import org.jboss.portletbridge.bridge.logger.BridgeLogger;
import org.jboss.portletbridge.bridge.logger.BridgeLogger.Level;
/**
* @author <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a>
*/
public class PortletExceptionHandler extends ExceptionHandlerWrapper {
private static final BridgeLogger logger = BridgeLoggerFactoryImpl.getLogger(PortletExceptionHandler.class.getName());
private ExceptionHandler wrapped;
public PortletExceptionHandler(ExceptionHandler exceptionHandler) {
this.wrapped = exceptionHandler;
}
/**
* @see javax.faces.context.ExceptionHandlerWrapper#getWrapped()
*/
@Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
@Override
public void handle() throws FacesException {
BridgeContext bridgeContext = BridgeContext.getCurrentInstance();
Map<Class<? extends Throwable>, String> errorViews = bridgeContext.getBridgeConfig().getFacesErrorViewMappings();
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
if (null != context) {
Throwable t = context.getException();
if (null != t) {
String errorView = getErrorView(t, errorViews);
if (null != errorView) {
FacesContext fc = FacesContext.getCurrentInstance();
try {
if (fc.getCurrentPhaseId().equals(PhaseId.RENDER_RESPONSE)) {
// if we are already redirecting from this error
// to this error, just skip
// this would happen if there's an error on the
// errorView and would cause an infinite loop
if (!errorView.equals(bridgeContext.getRedirectViewId())) {
// browser has seen already some content, so, we can't
// reset the buffer/response
if (!fc.getExternalContext().isResponseCommitted()) {
fc.getExternalContext().responseReset();
fc.getExternalContext().setResponseBufferSize(-1);
}
bridgeContext.setRedirectViewId(errorView);
bridgeContext.setRenderRedirect(true);
}
} else {
NavigationHandler nav = fc.getApplication().getNavigationHandler();
nav.handleNavigation(fc, null, errorView);
fc.renderResponse();
}
} finally {
i.remove();
}
} else {
logger.log(Level.ERROR, "No error mapping found in web.xml for Throwable: " + t.getClass().getName());
}
} else {
logger.log(Level.ERROR,
"Null exception found on ExceptionQueuedEventContext in Phase: " + context.getPhaseId()
+ " and Component:" + context.getComponent());
}
} else {
logger.log(Level.ERROR, "ExceptionQueuedEventContext null for ExceptionQueuedEvent: " + event.toString());
}
}
// Delegate exceptions without error view mappings back to default JSF2 Exception Handler
getWrapped().handle();
}
/**
* This method calls itself for the exception clause ( if that exists ). If no error page was defined for the clause, look
* it up in the view id's map.
*
* @param t Throwable to retrieve Faces view for
* @param errorViews Mapping of Throwable to Faces views built from web.xml
* @return String representing Faces View to display for Throwable
*/
protected String getErrorView(Throwable t, Map<Class<? extends Throwable>, String> errorViews) {
Throwable cause = getCause(t);
String errorView = null;
if (null != cause) {
errorView = getErrorView(cause, errorViews);
if (null != errorView) {
return errorView;
}
}
for (Class<? extends Throwable> errorClass : errorViews.keySet()) {
if (errorClass.isInstance(t) && errorViews.containsKey(errorClass)) {
errorView = errorViews.get(errorClass);
}
}
return errorView;
}
/**
* Get exception clause or ServletException rootClause.
*
* @param exception
* @return
*/
protected Throwable getCause(Throwable exception) {
Throwable cause = null;
if (exception instanceof ServletException) {
cause = ((ServletException) exception).getRootCause();
} else {
cause = exception.getCause();
}
return cause;
}
}