/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.myfaces.mc.test.core; import org.apache.myfaces.mc.test.core.mock.MockMyFacesViewDeclarationLanguageFactory; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.el.ExpressionFactory; import javax.faces.FacesException; import javax.faces.FactoryFinder; import javax.faces.application.Application; import javax.faces.application.FacesMessage; import javax.faces.application.ProjectStage; import javax.faces.application.ViewHandler; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.FacesContextFactory; import javax.faces.context.Flash; import javax.faces.event.ExceptionQueuedEvent; import javax.faces.event.ExceptionQueuedEventContext; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; import javax.faces.event.PreRenderViewEvent; import javax.faces.lifecycle.Lifecycle; import javax.faces.lifecycle.LifecycleFactory; import javax.faces.view.ViewDeclarationLanguage; import javax.faces.webapp.FacesServlet; import javax.naming.Context; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.http.HttpServletResponse; import org.apache.myfaces.config.ConfigFilesXmlValidationUtils; import org.apache.myfaces.config.DefaultFacesConfigurationProvider; import org.apache.myfaces.config.RuntimeConfig; import org.apache.myfaces.config.annotation.NoInjectionAnnotationLifecycleProvider; import org.apache.myfaces.config.element.FacesConfig; import org.apache.myfaces.config.impl.digester.elements.FactoryImpl; import org.apache.myfaces.lifecycle.LifecycleImpl; import org.apache.myfaces.lifecycle.ViewNotFoundException; import org.apache.myfaces.mc.test.core.annotation.DeclareFacesConfig; import org.apache.myfaces.mc.test.core.annotation.ManagedBeans; import org.apache.myfaces.mc.test.core.annotation.PageBean; import org.apache.myfaces.mc.test.core.annotation.TestConfig; import org.apache.myfaces.mc.test.core.mock.DefaultContext; import org.apache.myfaces.mc.test.core.mock.MockInitialContextFactory; import org.apache.myfaces.shared.config.MyfacesConfig; import org.apache.myfaces.shared.util.ClassUtils; import org.apache.myfaces.spi.FacesConfigurationProvider; import org.apache.myfaces.spi.impl.DefaultFacesConfigurationProviderFactory; import org.apache.myfaces.spi.impl.NoInjectionAnnotationInjectionProvider; import org.apache.myfaces.test.el.MockExpressionFactory; import org.apache.myfaces.test.mock.MockPrintWriter; import org.apache.myfaces.test.mock.MockServletConfig; import org.apache.myfaces.test.mock.MockServletContext; import org.apache.myfaces.test.mock.MockWebContainer; import org.apache.myfaces.webapp.AbstractFacesInitializer; import org.apache.myfaces.webapp.StartupServletContextListener; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.xml.sax.SAXException; /** *
Abstract JUnit test case base class, which sets up MyFaces Core environment * using mock object for the outer servlet environment.
*Since jsp engine is not bundled with MyFaces, this configuration is able to * handle facelet pages only.
* * @author Leonardo Uribe * */ public abstract class AbstractMyFacesTestCase { private static final Class> PHASE_EXECUTOR_CLASS; private static final Class> PHASE_MANAGER_CLASS; static { Class> phaseExecutorClass = null; Class> phaseManagerClass = null; try { phaseExecutorClass = Class.forName("org.apache.myfaces.lifecycle.PhaseExecutor"); phaseManagerClass = Class.forName("org.apache.myfaces.lifecycle.PhaseListenerManager"); } catch (ClassNotFoundException e) { //No op } PHASE_EXECUTOR_CLASS = phaseExecutorClass; PHASE_MANAGER_CLASS = phaseManagerClass; } public static final String PHASE_MANAGER_INSTANCE = "org.apache.myfaces.test.PHASE_MANAGER_INSTANCE"; public static final String LAST_PHASE_PROCESSED = "oam.LAST_PHASE_PROCESSED"; public static final String LAST_RENDER_PHASE_STEP = "oam.LAST_RENDER_PHASE_STEP"; public static final int BEFORE_RENDER_STEP = 1; public static final int BUILD_VIEW_CYCLE_STEP = 2; public static final int VIEWHANDLER_RENDER_STEP = 3; public static final int AFTER_RENDER_STEP = 4; // ------------------------------------------------------------ Constructors /** *Construct a new instance of this test case.
* * @param name Name of this test case */ public AbstractMyFacesTestCase() { } // ---------------------------------------------------- Overall Test Methods /** *Set up instance variables required by this test case.
*/ @Before public void setUp() throws Exception { // Set up a new thread context class loader setUpClassloader(); jsfConfiguration = sharedConfiguration.get(getTestJavaClass().getName()); if (jsfConfiguration == null) { jsfConfiguration = new SharedFacesConfiguration(); } TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class); boolean enableJNDI = (testConfig != null) ? testConfig.enableJNDI() : true; if (enableJNDI) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, MockInitialContextFactory.class.getName()); jndiContext = new DefaultContext(); MockInitialContextFactory.setCurrentContext(jndiContext); } // Set up Servlet API Objects setUpServletObjects(); // Set up JSF API Objects FactoryFinder.releaseFactories(); setUpServletListeners(); webContainer.contextInitialized(new ServletContextEvent(servletContext)); setUpFacesServlet(); sharedConfiguration.put(getTestJavaClass().getName(), jsfConfiguration); } /** * Set up the thread context classloader. JSF uses the this classloader * in order to find related factory classes and other resources, but in * some selected cases, the default classloader cannot be properly set. * * @throws Exception */ protected void setUpClassloader() throws Exception { // Set up a new thread context class loader threadContextClassLoader = Thread.currentThread() .getContextClassLoader(); Thread.currentThread() .setContextClassLoader( new URLClassLoader(new URL[0], this.getClass() .getClassLoader())); classLoaderSet = true; } /** *Setup servlet objects that will be used for the test:
* *servletConfig
(MockServletConfig
)servletContext
(MockServletContext
)Setup web config params. By default it sets the following params
* *Return an URI that identifies the base path that will be used by servletContext * to load resources like facelet files an others. By default it points to the directory * path calculated from the package name of the child test class.
* * @return */ protected URI getWebappContextURI() { try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); URL url = cl.getResource(getWebappResourcePath()); if (url == null) { throw new FileNotFoundException(cl.getResource("").getFile() + getWebappResourcePath() + " was not found"); } else { return new URI(url.toString()); } } catch (Exception e) { throw new RuntimeException("Error Initializing Context", e); } } /** * Return a path that is used to load resources like facelet files an others. * By default it points to the directory path calculated from the package * name of the child test class. * * @return */ protected String getWebappResourcePath() { TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class); if (testConfig != null && testConfig.webappResourcePath() != null && !"testClassResourcePackage".equals(testConfig.webappResourcePath())) { return testConfig.webappResourcePath(); } return this.getClass().getName().substring(0, this.getClass().getName().lastIndexOf('.')).replace('.', '/') + "/"; } /** * Create the ExpressionFactory instance that will be used to initialize the test * environment. By default it uses MockExpressionFactory. * * @return */ protected ExpressionFactory createExpressionFactory() { TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class); if (testConfig != null && testConfig.expressionFactory() != null && testConfig.expressionFactory().length() > 0) { return (ExpressionFactory) ClassUtils.newInstance( testConfig.expressionFactory(), ExpressionFactory.class); } return new MockExpressionFactory(); } /** * setup servlets avaliable in the test environment * * @throws Exception */ protected void setUpServlets() throws Exception { setUpFacesServlet(); } /** * setup listeners avaliable in the test environment * * @throws Exception */ protected void setUpServletListeners() throws Exception { setUpMyFaces(); } /** * * @return */ protected FacesConfigurationProvider createFacesConfigurationProvider() { return new MyFacesMockFacesConfigurationProvider(this); } protected AbstractFacesInitializer createFacesInitializer() { return new JUnitFacesInitializer(this); } protected void setUpMyFaces() throws Exception { if (facesConfigurationProvider == null) { facesConfigurationProvider = createFacesConfigurationProvider(); } servletContext.setAttribute( DefaultFacesConfigurationProviderFactory.FACES_CONFIGURATION_PROVIDER_INSTANCE_KEY, facesConfigurationProvider); listener = new StartupServletContextListener(); listener.setFacesInitializer(createFacesInitializer()); webContainer.subscribeListener(listener); //listener.contextInitialized(new ServletContextEvent(servletContext)); } protected void tearDownMyFaces() { //Don't tear down FacesConfigurationProvider, because that is shared by all tests. //This helps to reduce the time each test takes //facesConfigurationProvider = null //listener.contextDestroyed(new ServletContextEvent(servletContext)); } protected void setUpFacesServlet() throws Exception { lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); facesContextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY); lifecycle = lifecycleFactory.getLifecycle(getLifecycleId()); } protected void tearDownFacesServlet() throws Exception { lifecycleFactory = null; facesContextFactory = null; } protected void tearDownServlets() throws Exception { tearDownFacesServlet(); } protected void tearDownServletListeners() throws Exception { tearDownMyFaces(); } @After public void tearDown() throws Exception { tearDownServlets(); webContainer.contextDestroyed(new ServletContextEvent(servletContext)); tearDownServletListeners(); listener = null; servletConfig = null; servletContext = null; FactoryFinder.releaseFactories(); if (jndiContext != null) { MockInitialContextFactory.clearCurrentContext(); } tearDownClassloader(); } protected void tearDownClassloader() throws Exception { if (classLoaderSet) { Thread.currentThread().setContextClassLoader(threadContextClassLoader); threadContextClassLoader = null; classLoaderSet = false; } } @AfterClass public static void tearDownClass() { standardFacesConfig = null; sharedConfiguration.clear(); } private String getLifecycleId() { // 1. check for Servlet's init-param // 2. check for global context parameter // 3. use default Lifecycle Id, if none of them was provided String serLifecycleId = servletConfig.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR); String appLifecycleId = servletConfig.getServletContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR); appLifecycleId = serLifecycleId == null ? appLifecycleId : serLifecycleId; return appLifecycleId != null ? appLifecycleId : LifecycleFactory.DEFAULT_LIFECYCLE; } /** * Call lifecycle.execute(facesContext) * * @param facesContext */ public void processLifecycleExecute(FacesContext facesContext) { lifecycle.attachWindow(facesContext); lifecycle.execute(facesContext); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.INVOKE_APPLICATION); } /** * Execute restore view phase. * * @param facesContext * @throws Exception */ public void restoreView(FacesContext facesContext) { lifecycle.attachWindow(facesContext); executePhase(facesContext, PhaseId.RESTORE_VIEW); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.RESTORE_VIEW); } /** * Execute apply request values phase. If the responseComplete or renderResponse * flags are set, it returns without do any action. * * @param facesContext * @throws Exception */ public void applyRequestValues(FacesContext facesContext) { if (facesContext.getRenderResponse() || facesContext.getResponseComplete()) { return; } processRemainingPhasesBefore(facesContext, PhaseId.APPLY_REQUEST_VALUES); executePhase(facesContext, PhaseId.APPLY_REQUEST_VALUES); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.APPLY_REQUEST_VALUES); } /** * Execute process validations phase. If the responseComplete or renderResponse * flags are set, it returns without do any action. * * @param facesContext * @throws Exception */ public void processValidations(FacesContext facesContext) { if (facesContext.getRenderResponse() || facesContext.getResponseComplete()) { return; } processRemainingPhasesBefore(facesContext, PhaseId.PROCESS_VALIDATIONS); executePhase(facesContext, PhaseId.PROCESS_VALIDATIONS); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.PROCESS_VALIDATIONS); } /** * Execute update model phase. If the responseComplete or renderResponse * flags are set, it returns without do any action. * * @param facesContext * @throws Exception */ public void updateModelValues(FacesContext facesContext) { if (facesContext.getRenderResponse() || facesContext.getResponseComplete()) { return; } processRemainingPhasesBefore(facesContext, PhaseId.UPDATE_MODEL_VALUES); executePhase(facesContext, PhaseId.UPDATE_MODEL_VALUES); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.UPDATE_MODEL_VALUES); } /** * Execute invoke application phase. If the responseComplete or renderResponse * flags are set, it returns without do any action. * * @param facesContext * @throws Exception */ public void invokeApplication(FacesContext facesContext) { if (facesContext.getRenderResponse() || facesContext.getResponseComplete()) { return; } processRemainingPhasesBefore(facesContext, PhaseId.INVOKE_APPLICATION); executePhase(facesContext, PhaseId.INVOKE_APPLICATION); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.INVOKE_APPLICATION); } public void processLifecycleRender(FacesContext facesContext) { renderResponse(facesContext); } /** * Call lifecycle.render(facesContext) * * @param facesContext */ public void renderResponse(FacesContext facesContext) { processRemainingExecutePhases(facesContext); lifecycle.render(facesContext); facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.RENDER_RESPONSE); facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, AFTER_RENDER_STEP); } protected void processRemainingPhasesBefore(FacesContext facesContext, PhaseId phaseId) { PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); if (lastPhaseId == null) { if (!phaseId.equals(PhaseId.RESTORE_VIEW)) { restoreView(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); } else { // There are no phases before restore view return; } } if (PhaseId.APPLY_REQUEST_VALUES.equals(phaseId)) { return; } boolean continueProcess = false; if (continueProcess || PhaseId.RESTORE_VIEW.equals(lastPhaseId)) { applyRequestValues(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); continueProcess = true; } if (PhaseId.PROCESS_VALIDATIONS.equals(phaseId)) { return; } if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId)) { processValidations(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); continueProcess = true; } if (PhaseId.UPDATE_MODEL_VALUES.equals(phaseId)) { return; } if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId)) { updateModelValues(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); continueProcess = true; } if (PhaseId.INVOKE_APPLICATION.equals(phaseId)) { return; } if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId)) { invokeApplication(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); continueProcess = true; } if (PhaseId.RENDER_RESPONSE.equals(phaseId)) { return; } if (continueProcess || PhaseId.INVOKE_APPLICATION.equals(lastPhaseId)) { renderResponse(facesContext); lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); continueProcess = true; } } public void processRemainingExecutePhases(FacesContext facesContext) { PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); if (lastPhaseId == null) { processLifecycleExecute(facesContext); return; } else { boolean continueProcess = false; if (PhaseId.RESTORE_VIEW.equals(lastPhaseId)) { applyRequestValues(facesContext); continueProcess = true; } if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId)) { processValidations(facesContext); continueProcess = true; } if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId)) { updateModelValues(facesContext); continueProcess = true; } if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId)) { invokeApplication(facesContext); continueProcess = true; } } } public void processRemainingPhases(FacesContext facesContext) { PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED); if (lastPhaseId == null) { processLifecycleExecute(facesContext); renderResponse(facesContext); return; } else { boolean continueProcess = false; if (PhaseId.RESTORE_VIEW.equals(lastPhaseId)) { applyRequestValues(facesContext); continueProcess = true; } if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId)) { processValidations(facesContext); continueProcess = true; } if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId)) { updateModelValues(facesContext); continueProcess = true; } if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId)) { invokeApplication(facesContext); continueProcess = true; } if (continueProcess || PhaseId.INVOKE_APPLICATION.equals(lastPhaseId)) { Integer step = (Integer) facesContext.getAttributes().get(LAST_RENDER_PHASE_STEP); if (step == null) { renderResponse(facesContext); } else { if (BEFORE_RENDER_STEP == step.intValue()) { executeBuildViewCycle(facesContext); executeViewHandlerRender(facesContext); executeAfterRender(facesContext); } else if (BUILD_VIEW_CYCLE_STEP == step.intValue()) { executeViewHandlerRender(facesContext); executeAfterRender(facesContext); } else if (VIEWHANDLER_RENDER_STEP == step.intValue()) { executeAfterRender(facesContext); } } } } } /** * Indicate if annotation scanning should be done over the classpath. * By default it is set to false. * * @return */ protected boolean isScanAnnotations() { TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class); if (testConfig != null) { return testConfig.scanAnnotations(); } return false; } public void executeBeforeRender(FacesContext facesContext) { if (lifecycle instanceof LifecycleImpl) { LifecycleImpl lifecycleImpl = (LifecycleImpl) lifecycle; Object phaseExecutor = null; Object phaseManager = null; try { Field renderExecutorField = lifecycleImpl.getClass().getDeclaredField("renderExecutor"); if (!renderExecutorField.isAccessible()) { renderExecutorField.setAccessible(true); } phaseExecutor = renderExecutorField.get(lifecycleImpl); if (facesContext.getResponseComplete()) { return; } phaseManager = facesContext.getAttributes().get(PHASE_MANAGER_INSTANCE); if (phaseManager == null) { Method getPhaseListenersMethod = lifecycleImpl.getClass().getDeclaredMethod("getPhaseListeners"); if (!getPhaseListenersMethod.isAccessible()) { getPhaseListenersMethod.setAccessible(true); } Constructor> plmc = PHASE_MANAGER_CLASS.getDeclaredConstructor( new Class[]{Lifecycle.class, FacesContext.class, PhaseListener[].class}); if (!plmc.isAccessible()) { plmc.setAccessible(true); } phaseManager = plmc.newInstance(lifecycle, facesContext, getPhaseListenersMethod.invoke( lifecycleImpl, null)); facesContext.getAttributes().put(PHASE_MANAGER_INSTANCE, phaseManager); } } catch (NoSuchFieldException ex) { throw new IllegalStateException("Cannot get executors from LifecycleImpl", ex); } catch (SecurityException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } catch (IllegalArgumentException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } catch (IllegalAccessException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } catch (NoSuchMethodException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } catch (InvocationTargetException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } catch (InstantiationException ex) { throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex); } Flash flash = facesContext.getExternalContext().getFlash(); try { facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE); flash.doPrePhaseActions(facesContext); // let the PhaseExecutor do some pre-phase actions //renderExecutor.doPrePhaseActions(facesContext); Method doPrePhaseActionsMethod = phaseExecutor.getClass().getMethod("doPrePhaseActions", FacesContext.class); if(!(doPrePhaseActionsMethod.isAccessible())) { doPrePhaseActionsMethod.setAccessible(true); } doPrePhaseActionsMethod.invoke(phaseExecutor, facesContext); //phaseListenerMgr.informPhaseListenersBefore(PhaseId.RENDER_RESPONSE); Method informPhaseListenersBeforeMethod = phaseManager.getClass().getDeclaredMethod( "informPhaseListenersBefore", PhaseId.class); if(!(informPhaseListenersBeforeMethod.isAccessible())) { informPhaseListenersBeforeMethod.setAccessible(true); } informPhaseListenersBeforeMethod.invoke(phaseManager, PhaseId.RENDER_RESPONSE); // also possible that one of the listeners completed the response if (facesContext.getResponseComplete()) { return; } //renderExecutor.execute(facesContext); } catch (Throwable e) { // JSF 2.0: publish the executor's exception (if any). ExceptionQueuedEventContext context = new ExceptionQueuedEventContext ( facesContext, e, null, PhaseId.RENDER_RESPONSE); facesContext.getApplication().publishEvent (facesContext, ExceptionQueuedEvent.class, context); } finally { /* phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase()); flash.doPostPhaseActions(facesContext); // publish a field in the application map to indicate // that the first request has been processed requestProcessed(facesContext); */ } facesContext.getExceptionHandler().handle(); facesContext.getAttributes().remove(PHASE_MANAGER_INSTANCE); facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, BEFORE_RENDER_STEP); } else { throw new UnsupportedOperationException("Cannot execute phase on custom lifecycle instances"); } } public void executeBuildViewCycle(FacesContext facesContext) { Application application = facesContext.getApplication(); ViewHandler viewHandler = application.getViewHandler(); UIViewRoot root; UIViewRoot previousRoot; String viewId; String newViewId; boolean isNotSameRoot; int loops = 0; int maxLoops = 15; if (facesContext.getViewRoot() == null) { throw new ViewNotFoundException("A view is required to execute "+facesContext.getCurrentPhaseId()); } try { // do-while, because the view might change in PreRenderViewEvent-listeners do { root = facesContext.getViewRoot(); previousRoot = root; viewId = root.getViewId(); ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage( facesContext, viewId); if (vdl != null) { vdl.buildView(facesContext, root); } // publish a PreRenderViewEvent: note that the event listeners // of this event can change the view, so we have to perform the algorithm // until the viewId does not change when publishing this event. application.publishEvent(facesContext, PreRenderViewEvent.class, root); // was the response marked as complete by an event listener? if (facesContext.getResponseComplete()) { return; } root = facesContext.getViewRoot(); newViewId = root.getViewId(); isNotSameRoot = !( (newViewId == null ? newViewId == viewId : newViewId.equals(viewId) ) && previousRoot.equals(root) ); loops++; } while ((newViewId == null && viewId != null) || (newViewId != null && (!newViewId.equals(viewId) || isNotSameRoot ) ) && loops < maxLoops); if (loops == maxLoops) { // PreRenderView reach maxLoops - probably a infinitive recursion: boolean production = facesContext.isProjectStage(ProjectStage.Production); /* Level level = production ? Level.FINE : Level.WARNING; if (log.isLoggable(level)) { log.log(level, "Cicle over buildView-PreRenderViewEvent on RENDER_RESPONSE phase " + "reaches maximal limit, please check listeners for infinite recursion."); }*/ } facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, BUILD_VIEW_CYCLE_STEP); } catch (IOException e) { throw new FacesException(e.getMessage(), e); } } public void executeViewHandlerRender(FacesContext facesContext) { Application application = facesContext.getApplication(); ViewHandler viewHandler = application.getViewHandler(); try { viewHandler.renderView(facesContext, facesContext.getViewRoot()); // log all unhandled FacesMessages, don't swallow them // perf: org.apache.myfaces.context.servlet.FacesContextImpl.getMessageList() creates // new Collections.unmodifiableList with every invocation-> call it only once // and messageList is RandomAccess -> use index based loop List