/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.faces.context; import java.util.List; import java.util.ArrayList; import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerFactory; import javax.faces.event.ExceptionQueuedEvent; import javax.faces.event.ExceptionQueuedEventContext; import javax.faces.event.AbortProcessingException; import javax.faces.webapp.PreJsf2ExceptionHandlerFactory; import javax.faces.FacesException; import javax.el.ELException; import com.sun.faces.cactus.ServletFacesTestCase; /** * <p> * Test case for both {@link ExceptionHandlerImpl} and the <code>ExceptionHandler</code> * created by {@link javax.faces.webapp.PreJsf2ExceptionHandlerFactory}. * </p> * * <p> * Testing for the API implementation occurs here to leverage common code. * </p> * */ public class TestExceptionHandler extends ServletFacesTestCase { private ExceptionHandlerFactory implFactory = new ExceptionHandlerFactoryImpl(); private ExceptionHandlerFactory apiFactory = new PreJsf2ExceptionHandlerFactory(); // ------------------------------------------------------------ Constructors public TestExceptionHandler() { super("TestExceptionHandler"); } public TestExceptionHandler(String name) { super(name); } // ------------------------------------------------------------ Test Methods public void testIsListenerForSource() { testIsListenerForSource(implFactory.getExceptionHandler()); testIsListenerForSource(apiFactory.getExceptionHandler()); } public void testProcessEvent() { testProcessEvent(implFactory.getExceptionHandler()); testProcessEvent(apiFactory.getExceptionHandler()); } public void testHandle() { testHandleNoEventsQueued(implFactory.getExceptionHandler()); testHandleNoEventsQueued(apiFactory.getExceptionHandler()); testHandleAbortProcessingExceptionQueued(implFactory.getExceptionHandler()); testHandleAbortProcessingExceptionQueued(apiFactory.getExceptionHandler()); testHandleExceptionThrow(implFactory.getExceptionHandler()); testHandleExceptionThrow(apiFactory.getExceptionHandler()); testHandleBeforeAfterExceptions(implFactory.getExceptionHandler(), true); testHandleBeforeAfterExceptions(apiFactory.getExceptionHandler(), false); //test for issue 1263 boolean isProcessingEvents = getFacesContext().isProcessingEvents(); try { getFacesContext().setProcessingEvents(false); testHandleExceptionThrow(implFactory.getExceptionHandler()); testHandleExceptionThrow(apiFactory.getExceptionHandler()); } finally { getFacesContext().setProcessingEvents(isProcessingEvents); } } public void testGetRootCause() { testGetRootCauseNull(implFactory.getExceptionHandler()); testGetRootCauseNull(apiFactory.getExceptionHandler()); } // --------------------------------------------------------- Private Methods private void testIsListenerForSource(ExceptionHandler handler) { assertFalse(handler.isListenerForSource(null)); ExceptionQueuedEventContext ectx = new ExceptionQueuedEventContext(getFacesContext(), new RuntimeException()); assertFalse(handler.isListenerForSource(new ExceptionQueuedEvent(ectx))); assertTrue(handler.isListenerForSource(ectx)); } private void testProcessEvent(ExceptionHandler handler) { // if event is null, no action is taken, which means an empty Iterator // for the getUnhandledExceptionQueuedEvents handler.processEvent(null); List<ExceptionQueuedEvent> events = copyToList(handler.getUnhandledExceptionQueuedEvents()); assertTrue(events.isEmpty()); // queue an exception event... ExceptionQueuedEventContext ectx = new ExceptionQueuedEventContext(getFacesContext(), new RuntimeException()); handler.processEvent(new ExceptionQueuedEvent(ectx)); events = copyToList(handler.getUnhandledExceptionQueuedEvents()); assertTrue(events.size() == 1); assertTrue(events.get(0).getSource() == ectx); ExceptionQueuedEventContext ectx2 = new ExceptionQueuedEventContext(getFacesContext(), new RuntimeException()); // queue an additionl event to ensure order is maintained handler.processEvent(new ExceptionQueuedEvent(ectx2)); events = copyToList(handler.getUnhandledExceptionQueuedEvents()); assertTrue(events.size() == 2); assertTrue(events.get(0).getSource() == ectx); assertTrue(events.get(1).getSource() == ectx2); } private void testHandleNoEventsQueued(ExceptionHandler handler) { // no events have been queued try { handler.handle(); } catch (Throwable t) { assertTrue("Unexpected exception thrown with no ExceptionQueuedEvents queued", false); } } private void testHandleAbortProcessingExceptionQueued(ExceptionHandler handler) { getFacesContext().setExceptionHandler(handler); ExceptionQueuedEventContext ctx = new ExceptionQueuedEventContext(getFacesContext(), new AbortProcessingException()); // queue the abort processing exception. When calling handle(), no // exception should be thrown, but the ExceptionQueuedEvent should be returned // by getHandledExceptionQueuedEvents() while getUnhandledExceptionQueuedEvents() // should be null, and getHandledExceptionQueuedEvent() should return null // as nothing was thrown by the handle() method. // Side note, validate the exception is properly queued by publishing // and event. queueException(ctx); List<ExceptionQueuedEvent> unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); List<ExceptionQueuedEvent> handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(unhandled.size() == 1); assertTrue(handled.isEmpty()); try { handler.handle(); } catch (Throwable t) { assertTrue("Exception thrown by handle() when only an AbortProcessingException was queued. These should be ignored.", false); } unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertNull(handler.getHandledExceptionQueuedEvent()); assertTrue(unhandled.isEmpty()); assertTrue(handled.size() == 1); // queue another and call handled() again queueException(ctx); unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(unhandled.size() == 1); assertTrue(handled.size() == 1); try { handler.handle(); } catch (Throwable t) { assertTrue("Exception thrown by handle() when only an AbortProcessingException was queued. These should be ignored.", false); } unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(unhandled.size() == 0); assertTrue(handled.size() == 2); } public void testHandleExceptionThrow(ExceptionHandler handler) { getFacesContext().setExceptionHandler(handler); ExceptionQueuedEventContext abortProcessing = new ExceptionQueuedEventContext(getFacesContext(), new AbortProcessingException()); ExceptionQueuedEventContext abortProcessing2 = new ExceptionQueuedEventContext(getFacesContext(), new AbortProcessingException()); // wrap this up in a chain of exceptions to unsure that when // getRootCause() we get what we expect Exception e = new FacesException( new ELException( new FacesException( new IllegalStateException( new FacesException( new IllegalArgumentException()))))); ExceptionQueuedEventContext runtimeException = new ExceptionQueuedEventContext(getFacesContext(), e); ExceptionQueuedEventContext runtimeException2 = new ExceptionQueuedEventContext(getFacesContext(), e); ExceptionQueuedEventContext abortProcessing3 = new ExceptionQueuedEventContext(getFacesContext(), new AbortProcessingException()); queueException(abortProcessing); queueException(abortProcessing2); queueException(runtimeException); queueException(runtimeException2); queueException(abortProcessing3); List<ExceptionQueuedEvent> unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); List<ExceptionQueuedEvent> handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(unhandled.size() == 5); assertTrue(handled.isEmpty()); try { handler.handle(); assertTrue("No exception thrown by the handle() method, but there were RuntimeExceptions present that should have been thrown.", false); } catch (Throwable t) { assertTrue(t instanceof FacesException); Throwable root = t.getCause(); assertTrue(root instanceof IllegalStateException); root = root.getCause(); assertTrue(root instanceof FacesException); root = root.getCause(); assertTrue(root instanceof IllegalArgumentException); root = root.getCause(); assertNull(root); } assertTrue(handler.getHandledExceptionQueuedEvent().getSource() == runtimeException); unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(handled.size() == 3); assertTrue(unhandled.size() == 2); assertTrue(handled.get(0).getSource() == abortProcessing); assertTrue(handled.get(1).getSource() == abortProcessing2); assertTrue(handled.get(2).getSource() == runtimeException); assertTrue(unhandled.get(0).getSource() == runtimeException2); assertTrue(unhandled.get(1).getSource() == abortProcessing3); // call handle() again and make sure the results are sane try { handler.handle(); assertTrue("No exception thrown by the handle() method, but there were RuntimeExceptions present that should have been thrown.", false); } catch (Throwable t) { // expected } assertTrue(handler.getHandledExceptionQueuedEvent().getSource() == runtimeException2); unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(handled.size() == 4); assertTrue(unhandled.size() == 1); assertTrue(handled.get(0).getSource() == abortProcessing); assertTrue(handled.get(1).getSource() == abortProcessing2); assertTrue(handled.get(2).getSource() == runtimeException); assertTrue(handled.get(3).getSource() == runtimeException2); assertTrue(unhandled.get(0).getSource() == abortProcessing3); // call handle() again and make sure the results are sane - no exception thrown // this time try { handler.handle(); } catch (Throwable t) { assertTrue("Exception thrown by handle() when only an AbortProcessingException was queued. These should be ignored.", false); } assertTrue(handler.getHandledExceptionQueuedEvent().getSource() == runtimeException2); unhandled = copyToList(handler.getUnhandledExceptionQueuedEvents()); handled = copyToList(handler.getHandledExceptionQueuedEvents()); assertTrue(handled.size() == 5); assertTrue(unhandled.isEmpty()); assertTrue(handled.get(0).getSource() == abortProcessing); assertTrue(handled.get(1).getSource() == abortProcessing2); assertTrue(handled.get(2).getSource() == runtimeException); assertTrue(handled.get(3).getSource() == runtimeException2); assertTrue(handled.get(4).getSource() == abortProcessing3); } private void testHandleBeforeAfterExceptions(ExceptionHandler handler, boolean shouldThrow) { // In 2.0, exceptions thrown by before or after phases of a single // phase will be rethrown. In 1.2, they were logged and swallowed. // Make sure this is the case. getFacesContext().setExceptionHandler(handler); ExceptionQueuedEventContext ctx = new ExceptionQueuedEventContext(getFacesContext(), new RuntimeException()); ctx.getAttributes().put(ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY, Boolean.TRUE); queueException(ctx); try { handler.handle(); if (shouldThrow) { assertTrue("[BEFORE] Exception expected to be thrown", false); } } catch (Throwable t) { if (!shouldThrow) { assertTrue("[BEFORE] Exception should not have been thrown", false); } } ctx.getAttributes().remove(ExceptionQueuedEventContext.IN_BEFORE_PHASE_KEY); ctx.getAttributes().put(ExceptionQueuedEventContext.IN_AFTER_PHASE_KEY, Boolean.TRUE); try { handler.handle(); if (shouldThrow) { assertTrue("[AFTER] Exception expected to be thrown", false); } } catch (Throwable t) { if (!shouldThrow) { assertTrue("[AFTER] Exception should not have been thrown", false); } } } private void testGetRootCauseNull(ExceptionHandler handler) { assertNull(handler.getRootCause(null)); } private List<ExceptionQueuedEvent> copyToList(Iterable<ExceptionQueuedEvent> events) { List<ExceptionQueuedEvent> list = new ArrayList<ExceptionQueuedEvent>(); for (ExceptionQueuedEvent event : events) { list.add(event); } return list; } private void queueException(ExceptionQueuedEventContext source) { getFacesContext().getApplication().publishEvent(getFacesContext(), ExceptionQueuedEvent.class, source); } }