/* * Copyright 2017 OmniFaces * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package org.omnifaces.filter; import static java.util.logging.Level.FINE; import static org.omnifaces.util.Exceptions.unwrap; import java.io.FileNotFoundException; import java.io.IOException; import java.util.logging.Logger; import javax.el.ELException; import javax.faces.FacesException; import javax.faces.webapp.FacesServlet; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.omnifaces.exceptionhandler.FullAjaxExceptionHandler; /** * <p> * The {@link FacesExceptionFilter} will solve 2 problems with exceptions thrown in JSF methods. * <ol> * <li>Mojarra's <code>FacesFileNotFoundException</code> needs to be interpreted as 404. * <li>Root cause needs to be unwrapped from {@link FacesException} and {@link ELException} to utilize standard * Servlet API error page handling. * </ol> * <p> * Noted should be that this filter won't run on exceptions thrown during ajax requests. To handle them using * <code>web.xml</code> configured error pages, use {@link FullAjaxExceptionHandler}. * * <h3>Installation</h3> * <p> * To get it to run, map this filter on the <code><servlet-name></code> of the {@link FacesServlet} in the same * <code>web.xml</code>. * <pre> * <filter> * <filter-name>facesExceptionFilter</filter-name> * <filter-class>org.omnifaces.filter.FacesExceptionFilter</filter-class> * </filter> * <filter-mapping> * <filter-name>facesExceptionFilter</filter-name> * <servlet-name>facesServlet</servlet-name> * </filter-mapping> * </pre> * * <h3>Configuration</h3> * <p> * By default only {@link FacesException} and {@link ELException} are unwrapped. You can supply a context parameter * {@value org.omnifaces.exceptionhandler.FullAjaxExceptionHandler#PARAM_NAME_EXCEPTION_TYPES_TO_UNWRAP} to specify * additional exception types to unwrap. The context parameter value must be a commaseparated string of fully qualified * names of additional exception types. * <pre> * <context-param> * <param-name>org.omnifaces.EXCEPTION_TYPES_TO_UNWRAP</param-name> * <param-value>javax.ejb.EJBException,javax.persistence.RollbackException</param-value> * </context-param> * </pre> * <p> * This context parameter will also be read and used by {@link FullAjaxExceptionHandler}. * * * @author Bauke Scholtz * @see FullAjaxExceptionHandler * @see HttpFilter */ public class FacesExceptionFilter extends HttpFilter { private static final Logger logger = Logger.getLogger(FacesExceptionFilter.class.getName()); private Class<? extends Throwable>[] exceptionTypesToUnwrap; @Override public void init() throws ServletException { exceptionTypesToUnwrap = FullAjaxExceptionHandler.getExceptionTypesToUnwrap(getServletContext()); } @Override public void doFilter (HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException { try { chain.doFilter(request, response); } catch (FileNotFoundException ignore) { logger.log(FINE, "Ignoring thrown exception; this is a Mojarra quirk and it should be interpreted as 404.", ignore); response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); } catch (ServletException ignore) { logger.log(FINE, "Ignoring thrown exception; this is a wrapper exception and only its root cause is of interest.", ignore); throw new ServletException(unwrap(ignore.getRootCause(), exceptionTypesToUnwrap)); } } }