View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.mc.test.core.runner;
20  
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.net.URI;
29  import java.net.URL;
30  import java.net.URLClassLoader;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.ConcurrentHashMap;
34  import java.util.logging.Level;
35  import java.util.logging.Logger;
36  import javax.el.ExpressionFactory;
37  import javax.faces.FacesException;
38  import javax.faces.FactoryFinder;
39  import javax.faces.application.Application;
40  import javax.faces.application.FacesMessage;
41  import javax.faces.application.ProjectStage;
42  import javax.faces.application.ViewHandler;
43  import javax.faces.component.UIViewRoot;
44  import javax.faces.context.ExternalContext;
45  import javax.faces.context.FacesContext;
46  import javax.faces.context.FacesContextFactory;
47  import javax.faces.context.Flash;
48  import javax.faces.event.ExceptionQueuedEvent;
49  import javax.faces.event.ExceptionQueuedEventContext;
50  import javax.faces.event.PhaseId;
51  import javax.faces.event.PhaseListener;
52  import javax.faces.event.PreRenderViewEvent;
53  import javax.faces.lifecycle.Lifecycle;
54  import javax.faces.lifecycle.LifecycleFactory;
55  import javax.faces.view.ViewDeclarationLanguage;
56  import javax.faces.webapp.FacesServlet;
57  import javax.naming.Context;
58  import javax.servlet.ServletContext;
59  import javax.servlet.ServletContextEvent;
60  import javax.servlet.ServletContextListener;
61  import javax.servlet.http.HttpServletResponse;
62  import org.apache.myfaces.config.ConfigFilesXmlValidationUtils;
63  import org.apache.myfaces.config.DefaultFacesConfigurationProvider;
64  import org.apache.myfaces.config.RuntimeConfig;
65  import org.apache.myfaces.config.annotation.NoInjectionAnnotationLifecycleProvider;
66  import org.apache.myfaces.config.element.FacesConfig;
67  import org.apache.myfaces.config.impl.digester.elements.FactoryImpl;
68  import org.apache.myfaces.lifecycle.LifecycleImpl;
69  import org.apache.myfaces.lifecycle.ViewNotFoundException;
70  import org.apache.myfaces.mc.test.core.annotation.BeforeJSFInit;
71  import org.apache.myfaces.mc.test.core.mock.MockMyFacesViewDeclarationLanguageFactory;
72  import org.apache.myfaces.mc.test.core.annotation.DeclareFacesConfig;
73  import org.apache.myfaces.mc.test.core.annotation.ManagedBeans;
74  import org.apache.myfaces.mc.test.core.annotation.TestConfig;
75  import org.apache.myfaces.mc.test.core.annotation.PageBean;
76  import org.apache.myfaces.mc.test.core.annotation.SetupWebConfigParams;
77  import org.apache.myfaces.mc.test.core.annotation.TestServletListeners;
78  import org.apache.myfaces.mc.test.core.mock.DefaultContext;
79  import org.apache.myfaces.mc.test.core.mock.MockInitialContextFactory;
80  import org.apache.myfaces.shared.config.MyfacesConfig;
81  import org.apache.myfaces.shared.util.ClassUtils;
82  import org.apache.myfaces.spi.FacesConfigurationProvider;
83  import org.apache.myfaces.spi.InjectionProvider;
84  import org.apache.myfaces.spi.impl.CDIAnnotationDelegateInjectionProvider;
85  import org.apache.myfaces.spi.impl.DefaultFacesConfigurationProviderFactory;
86  import org.apache.myfaces.spi.impl.NoInjectionAnnotationInjectionProvider;
87  import org.apache.myfaces.test.el.MockExpressionFactory;
88  import org.apache.myfaces.test.mock.MockPrintWriter;
89  import org.apache.myfaces.test.mock.MockServletConfig;
90  import org.apache.myfaces.test.mock.MockServletContext;
91  import org.apache.myfaces.test.mock.MockWebContainer;
92  import org.apache.myfaces.util.ExternalSpecifications;
93  import org.apache.myfaces.webapp.AbstractFacesInitializer;
94  import org.apache.myfaces.webapp.FacesInitializer;
95  import org.apache.myfaces.webapp.StartupServletContextListener;
96  import org.junit.runners.model.FrameworkMethod;
97  import org.junit.runners.model.TestClass;
98  import org.xml.sax.SAXException;
99  
100 /**
101  *
102  */
103 public class AbstractJsfTestContainer
104 {
105     private static final Class<?> PHASE_EXECUTOR_CLASS;
106     private static final Class<?> PHASE_MANAGER_CLASS;
107     
108     static {
109         Class<?> phaseExecutorClass = null;
110         Class<?> phaseManagerClass = null;
111         try
112         {
113             phaseExecutorClass = Class.forName("org.apache.myfaces.lifecycle.PhaseExecutor");
114             phaseManagerClass = Class.forName("org.apache.myfaces.lifecycle.PhaseListenerManager");
115         }
116         catch (ClassNotFoundException e)
117         {
118             //No op
119         }
120         PHASE_EXECUTOR_CLASS = phaseExecutorClass;
121         PHASE_MANAGER_CLASS = phaseManagerClass;
122     }
123     
124     public static final String PHASE_MANAGER_INSTANCE = "org.apache.myfaces.test.PHASE_MANAGER_INSTANCE";
125     
126     public static final String LAST_PHASE_PROCESSED = "oam.LAST_PHASE_PROCESSED";
127     
128     public static final String LAST_RENDER_PHASE_STEP = "oam.LAST_RENDER_PHASE_STEP";
129     
130     public static final int BEFORE_RENDER_STEP = 1;
131     public static final int BUILD_VIEW_CYCLE_STEP = 2;
132     public static final int VIEWHANDLER_RENDER_STEP = 3;
133     public static final int AFTER_RENDER_STEP = 4;
134     
135     // ------------------------------------------------------------ Constructors
136 
137     /**
138      * <p>Construct a new instance of this test case.</p>
139      *
140      * @param name Name of this test case
141      */    
142     public AbstractJsfTestContainer(TestClass testClass)
143     {
144         this.testClass = testClass;
145     }
146 
147     // ---------------------------------------------------- Overall Test Methods
148 
149     /**
150      * <p>Set up instance variables required by this test case.</p>
151      */
152     //@Before
153     public void setUp(Object testInstance)
154     {
155         this.testInstance = testInstance;
156         
157         setUpClassloader();
158         
159         jsfConfiguration = sharedConfiguration.get(getTestJavaClass().getName());
160         if (jsfConfiguration == null)
161         {
162             jsfConfiguration = new SharedFacesConfiguration();
163         }
164         
165         TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class);
166         boolean enableJNDI = (testConfig != null) ? testConfig.enableJNDI() : true;
167         if (enableJNDI)
168         {
169             System.setProperty(Context.INITIAL_CONTEXT_FACTORY, MockInitialContextFactory.class.getName());
170             jndiContext = new DefaultContext();
171             MockInitialContextFactory.setCurrentContext(jndiContext);
172         }
173 
174         // Set up Servlet API Objects
175         setUpServletObjects();
176 
177         // Set up JSF API Objects
178         FactoryFinder.releaseFactories();
179 
180         setUpServletListeners();
181         
182         webContainer.contextInitialized(new ServletContextEvent(servletContext));
183         
184         setUpFacesServlet();
185         
186         sharedConfiguration.put(getTestJavaClass().getName(), jsfConfiguration);
187     }
188     
189     /**
190      * Set up the thread context classloader. JSF uses the this classloader
191      * in order to find related factory classes and other resources, but in
192      * some selected cases, the default classloader cannot be properly set.
193      * 
194      * @throws Exception 
195      */
196     protected void setUpClassloader()
197     {
198         // Set up a new thread context class loader
199         threadContextClassLoader = Thread.currentThread()
200                 .getContextClassLoader();
201         Thread.currentThread()
202                 .setContextClassLoader(
203                         new URLClassLoader(new URL[0], this.getClass()
204                                 .getClassLoader()));
205         classLoaderSet = true;
206     }
207     
208     /**
209      * <p>Setup servlet objects that will be used for the test:</p>
210      * 
211      * <ul>
212      * <li><code>servletConfig</code> (<code>MockServletConfig</code>)</li>
213      * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
214      * </ul>
215      * 
216      * @throws Exception
217      */
218     protected void setUpServletObjects()
219     {
220         servletContext = new MockServletContext();
221         servletConfig = new MockServletConfig(servletContext);
222         servletContext.setDocumentRoot(getWebappContextURI());
223         webContainer = new MockWebContainer();
224         servletContext.setWebContainer(webContainer);
225         setUpWebConfigParams();
226     }
227     
228     /**
229      * <p>Setup web config params. By default it sets the following params</p>
230      * 
231      * <ul>
232      * <li>"org.apache.myfaces.INITIALIZE_ALWAYS_STANDALONE", "true"</li>
233      * <li>"javax.faces.PROJECT_STAGE", "UnitTest"</li>
234      * <li>"javax.faces.PARTIAL_STATE_SAVING", "true"</li>
235      * <li>"javax.faces.FACELETS_REFRESH_PERIOD", "-1"</li>
236      * </ul>
237      * 
238      * @throws Exception
239      */
240     protected void setUpWebConfigParams()
241     {
242         // Required parameters
243         servletContext.addInitParameter("org.apache.myfaces.INITIALIZE_ALWAYS_STANDALONE", "true");
244         servletContext.addInitParameter("javax.faces.PROJECT_STAGE", "UnitTest");
245         servletContext.addInitParameter("javax.faces.PARTIAL_STATE_SAVING", "true");
246         servletContext.addInitParameter(ViewHandler.FACELETS_REFRESH_PERIOD_PARAM_NAME,"-1");
247         servletContext.addInitParameter("org.apache.myfaces.config.annotation.LifecycleProvider",
248             NoInjectionAnnotationLifecycleProvider.class.getName());
249         
250         TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class);
251         if (testConfig != null && testConfig.oamAnnotationScanPackages() != null &&
252             testConfig.oamAnnotationScanPackages().length() > 0)
253         {
254             servletContext.addInitParameter("org.apache.myfaces.annotation.SCAN_PACKAGES",
255                 testConfig.oamAnnotationScanPackages());
256         }
257 
258         List<FrameworkMethod> setupWebConfigParamMethods = testClass.getAnnotatedMethods(SetupWebConfigParams.class);
259         if (setupWebConfigParamMethods != null && !setupWebConfigParamMethods.isEmpty())
260         {
261             for (FrameworkMethod fm : setupWebConfigParamMethods)
262             {
263                 try
264                 {
265                     fm.invokeExplosively(testInstance);
266                 }
267                 catch (Throwable ex)
268                 {
269                     throw new FacesException(ex);
270                 }
271             }
272         }
273     }
274     
275     /**
276      * <p>Return an URI that identifies the base path that will be used by servletContext
277      * to load resources like facelet files an others. By default it points to the directory
278      * path calculated from the package name of the child test class.</p>
279      * 
280      * @return
281      */
282     protected URI getWebappContextURI()
283     {
284         try
285         {
286             ClassLoader cl = Thread.currentThread().getContextClassLoader();
287             URL url = cl.getResource(getWebappResourcePath());
288             if (url == null)
289             {
290                 throw new FileNotFoundException(cl.getResource("").getFile()
291                         + getWebappResourcePath() + " was not found");
292             }
293             else
294             {
295                 return new URI(url.toString());
296             }
297         }
298         catch (Exception e)
299         {
300             throw new RuntimeException("Error Initializing Context", e);
301         }
302     }
303     
304     /**
305      * Return a path that is used to load resources like facelet files an others.
306      * By default it points to the directory path calculated from the package 
307      * name of the child test class.
308      * 
309      * @return
310      */
311     protected String getWebappResourcePath()
312     {
313         TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class);
314         if (testConfig != null && testConfig.webappResourcePath() != null &&
315             !"testClassResourcePackage".equals(testConfig.webappResourcePath()))
316         {
317             return testConfig.webappResourcePath();
318         }
319         return getTestJavaClass().getName().substring(0,
320                 getTestJavaClass().getName().lastIndexOf('.')).replace('.', '/')
321                 + "/";
322     }
323     
324     /**
325      * Create the ExpressionFactory instance that will be used to initialize the test
326      * environment. By default it uses MockExpressionFactory. 
327      * 
328      * @return
329      */
330     protected ExpressionFactory createExpressionFactory()
331     {
332         TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class);
333         if (testConfig != null && testConfig.expressionFactory() != null &&
334             testConfig.expressionFactory().length() > 0)
335         {
336             return (ExpressionFactory) ClassUtils.newInstance(
337                 testConfig.expressionFactory(), ExpressionFactory.class);
338         }
339         return new MockExpressionFactory();
340     }
341     
342     /**
343      * setup servlets avaliable in the test environment
344      * 
345      * @throws Exception
346      */
347     protected void setUpServlets() throws Exception
348     {
349         setUpFacesServlet();
350     }
351     
352     /**
353      * setup listeners avaliable in the test environment
354      * 
355      * @throws Exception
356      */
357     protected void setUpServletListeners()
358     {
359         TestServletListeners testServletListeners = getTestJavaClass().getAnnotation(TestServletListeners.class);
360         if (testServletListeners != null && testServletListeners.value() != null)
361         {
362             for (String listener : testServletListeners.value())
363             {
364                 try
365                 {
366                     webContainer.subscribeListener(listener);
367                 }
368                 catch (Exception ex)
369                 {
370                     throw new FacesException(ex);
371                 }
372             }
373         }
374 
375         // Subscribe a listener so we can trigger a method after all listeners but before initialize MyFaces
376         webContainer.subscribeListener(new ServletContextListener()
377         {
378             @Override
379             public void contextInitialized(ServletContextEvent sce)
380             {
381                 List<FrameworkMethod> setupWebConfigParamMethods = testClass.getAnnotatedMethods(BeforeJSFInit.class);
382                 if (setupWebConfigParamMethods != null && !setupWebConfigParamMethods.isEmpty())
383                 {
384                     for (FrameworkMethod fm : setupWebConfigParamMethods)
385                     {
386                         try
387                         {
388                             fm.invokeExplosively(testInstance);
389                         }
390                         catch (Throwable ex)
391                         {
392                             throw new FacesException(ex);
393                         }
394                     }
395                 }
396             }
397 
398             @Override
399             public void contextDestroyed(ServletContextEvent sce)
400             {
401             }
402             
403         });
404 
405         //owbListener = new WebBeansConfigurationListener();
406         //webContainer.subscribeListener(owbListener);
407         setUpMyFaces();
408     }
409     
410     /**
411      * 
412      * @return
413      */
414     protected FacesConfigurationProvider createFacesConfigurationProvider()
415     {
416         return new MyFacesMockFacesConfigurationProvider(); 
417     }
418     
419     protected AbstractFacesInitializer createFacesInitializer()
420     {
421         return new JUnitFacesInitializer(this);
422     }
423     
424     protected void setUpMyFaces()
425     {
426         if (facesConfigurationProvider == null)
427         {
428             facesConfigurationProvider = createFacesConfigurationProvider();
429         }
430         servletContext.setAttribute(
431                 DefaultFacesConfigurationProviderFactory.FACES_CONFIGURATION_PROVIDER_INSTANCE_KEY, 
432                 facesConfigurationProvider);
433         listener = new StartupServletContextListener();
434         listener.setFacesInitializer(getFacesInitializer());
435         webContainer.subscribeListener(listener);
436         //listener.contextInitialized(new ServletContextEvent(servletContext));
437     }
438 
439     protected void tearDownMyFaces()
440     {
441         //Don't tear down FacesConfigurationProvider, because that is shared by all tests.
442         //This helps to reduce the time each test takes 
443         //facesConfigurationProvider = null
444         
445         //listener.contextDestroyed(new ServletContextEvent(servletContext));
446     }
447 
448     protected void setUpFacesServlet()
449     {
450         lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
451         facesContextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
452         lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
453     }
454     
455     protected void tearDownFacesServlet()
456     {
457         lifecycleFactory = null;
458         facesContextFactory = null;
459     }
460     
461     protected void tearDownServlets()
462     {
463         tearDownFacesServlet();
464     }
465     
466     protected void tearDownServletListeners()
467     {
468         tearDownMyFaces();
469         //owbListener = null;
470     }
471     //@After
472     public void tearDown()
473     {
474         tearDownServlets();
475 
476         webContainer.contextDestroyed(new ServletContextEvent(servletContext));
477         
478         tearDownServletListeners();
479         
480         listener = null;
481         
482         servletConfig = null;
483         servletContext = null;
484         
485         FactoryFinder.releaseFactories();
486         
487         if (jndiContext != null)
488         {
489             MockInitialContextFactory.clearCurrentContext();
490         }
491         
492         tearDownClassloader();
493     }
494     
495     protected void tearDownClassloader()
496     {
497         if (classLoaderSet)
498         {
499             Thread.currentThread().setContextClassLoader(threadContextClassLoader);
500             threadContextClassLoader = null;
501             classLoaderSet = false;
502         }
503     }    
504     
505     //@AfterClass
506     public static void tearDownClass()
507     {
508         standardFacesConfig = null;
509         sharedConfiguration.clear();
510     }
511     
512     public static void tearDownClass(Class<?> targetTestClass)
513     {
514         sharedConfiguration.remove(targetTestClass);
515     }    
516     
517     private String getLifecycleId()
518     {
519         // 1. check for Servlet's init-param
520         // 2. check for global context parameter
521         // 3. use default Lifecycle Id, if none of them was provided
522         String serLifecycleId = servletConfig.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
523         String appLifecycleId = servletConfig.getServletContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
524         appLifecycleId = serLifecycleId == null ? appLifecycleId : serLifecycleId;
525         return appLifecycleId != null ? appLifecycleId : LifecycleFactory.DEFAULT_LIFECYCLE;
526     }
527 
528     /**
529      * Call lifecycle.execute(facesContext)
530      * 
531      * @param facesContext
532      */
533     public void processLifecycleExecute(FacesContext facesContext)
534     {
535         lifecycle.attachWindow(facesContext);
536         lifecycle.execute(facesContext);
537         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.INVOKE_APPLICATION);
538     }
539 
540     /**
541      * Execute restore view phase.
542      * 
543      * @param facesContext
544      * @throws Exception
545      */
546     public void restoreView(FacesContext facesContext)
547     {
548         lifecycle.attachWindow(facesContext);
549         executePhase(facesContext, PhaseId.RESTORE_VIEW);
550         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.RESTORE_VIEW);
551     }
552     
553     /**
554      * Execute apply request values phase. If the responseComplete or renderResponse
555      * flags are set, it returns without do any action.
556      * 
557      * @param facesContext
558      * @throws Exception
559      */
560     public void applyRequestValues(FacesContext facesContext)
561     {
562         if (facesContext.getRenderResponse() || facesContext.getResponseComplete())
563         {
564             return;
565         }
566         processRemainingPhasesBefore(facesContext, PhaseId.APPLY_REQUEST_VALUES);
567         executePhase(facesContext, PhaseId.APPLY_REQUEST_VALUES);
568         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.APPLY_REQUEST_VALUES);
569     }
570 
571     /**
572      * Execute process validations phase. If the responseComplete or renderResponse
573      * flags are set, it returns without do any action.
574      * 
575      * @param facesContext
576      * @throws Exception
577      */
578     public void processValidations(FacesContext facesContext)
579     {
580         if (facesContext.getRenderResponse() || facesContext.getResponseComplete())
581         {
582             return;
583         }
584         processRemainingPhasesBefore(facesContext, PhaseId.PROCESS_VALIDATIONS);
585         executePhase(facesContext, PhaseId.PROCESS_VALIDATIONS);
586         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.PROCESS_VALIDATIONS);
587     }
588 
589     /**
590      * Execute update model phase. If the responseComplete or renderResponse
591      * flags are set, it returns without do any action.
592      * 
593      * @param facesContext
594      * @throws Exception
595      */
596     public void updateModelValues(FacesContext facesContext)
597     {
598         if (facesContext.getRenderResponse() || facesContext.getResponseComplete())
599         {
600             return;
601         }
602         processRemainingPhasesBefore(facesContext, PhaseId.UPDATE_MODEL_VALUES);
603         executePhase(facesContext, PhaseId.UPDATE_MODEL_VALUES);
604         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.UPDATE_MODEL_VALUES);
605 
606     }
607     
608     /**
609      * Execute invoke application phase. If the responseComplete or renderResponse
610      * flags are set, it returns without do any action.
611      * 
612      * @param facesContext
613      * @throws Exception
614      */
615     public void invokeApplication(FacesContext facesContext)
616     {
617         if (facesContext.getRenderResponse() || facesContext.getResponseComplete())
618         {
619             return;
620         }
621         processRemainingPhasesBefore(facesContext, PhaseId.INVOKE_APPLICATION);
622         executePhase(facesContext, PhaseId.INVOKE_APPLICATION);
623         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.INVOKE_APPLICATION);
624     }
625 
626     public void processLifecycleRender(FacesContext facesContext)
627     {
628         renderResponse(facesContext);
629     }
630 
631     /**
632      * Call lifecycle.render(facesContext)
633      * 
634      * @param facesContext
635      */
636     public void renderResponse(FacesContext facesContext)
637     {
638         processRemainingExecutePhases(facesContext);
639         lifecycle.render(facesContext);
640         facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.RENDER_RESPONSE);
641         facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, AFTER_RENDER_STEP);
642     }
643     
644     protected void processRemainingPhasesBefore(FacesContext facesContext, PhaseId phaseId)
645     {
646         PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
647         if (lastPhaseId == null)
648         {
649             if (!phaseId.equals(PhaseId.RESTORE_VIEW))
650             {
651                 restoreView(facesContext);
652                 lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
653             }
654             else
655             {
656                 // There are no phases before restore view
657                 return;
658             }
659         }
660         if (PhaseId.APPLY_REQUEST_VALUES.equals(phaseId))
661         {
662             return;
663         }
664         boolean continueProcess = false;
665         if (continueProcess || PhaseId.RESTORE_VIEW.equals(lastPhaseId))
666         {
667             applyRequestValues(facesContext);
668             lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
669             continueProcess = true;
670         }
671         if (PhaseId.PROCESS_VALIDATIONS.equals(phaseId))
672         {
673             return;
674         }
675         if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId))
676         {
677             processValidations(facesContext);
678             lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
679             continueProcess = true;
680         }
681         if (PhaseId.UPDATE_MODEL_VALUES.equals(phaseId))
682         {
683             return;
684         }
685         if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId))
686         {
687             updateModelValues(facesContext);
688             lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
689             continueProcess = true;
690         }
691         if (PhaseId.INVOKE_APPLICATION.equals(phaseId))
692         {
693             return;
694         }
695         if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId))
696         {
697             invokeApplication(facesContext);
698             lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
699             continueProcess = true;
700         }        
701         if (PhaseId.RENDER_RESPONSE.equals(phaseId))
702         {
703             return;
704         }
705         if (continueProcess || PhaseId.INVOKE_APPLICATION.equals(lastPhaseId))
706         {
707             renderResponse(facesContext);
708             lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
709             continueProcess = true;
710         }
711     }
712     
713     public void processRemainingExecutePhases(FacesContext facesContext)
714     {
715         PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
716         if (lastPhaseId == null)
717         {
718             processLifecycleExecute(facesContext);
719             return;
720         }
721         else
722         {
723             boolean continueProcess = false;
724             if (PhaseId.RESTORE_VIEW.equals(lastPhaseId))
725             {
726                 applyRequestValues(facesContext);
727                 continueProcess = true;
728             }
729             if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId))
730             {
731                 processValidations(facesContext);
732                 continueProcess = true;
733             }
734             if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId))
735             {
736                 updateModelValues(facesContext);
737                 continueProcess = true;
738             }
739             if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId))
740             {
741                 invokeApplication(facesContext);
742                 continueProcess = true;
743             }
744         }
745     }
746 
747     public void processRemainingPhases(FacesContext facesContext)
748     {
749         PhaseId lastPhaseId = (PhaseId) facesContext.getAttributes().get(LAST_PHASE_PROCESSED);
750         if (lastPhaseId == null)
751         {
752             processLifecycleExecute(facesContext);
753             renderResponse(facesContext);
754             return;
755         }
756         else
757         {
758             boolean continueProcess = false;
759             if (PhaseId.RESTORE_VIEW.equals(lastPhaseId))
760             {
761                 applyRequestValues(facesContext);
762                 continueProcess = true;
763             }
764             if (continueProcess || PhaseId.APPLY_REQUEST_VALUES.equals(lastPhaseId))
765             {
766                 processValidations(facesContext);
767                 continueProcess = true;
768             }
769             if (continueProcess || PhaseId.PROCESS_VALIDATIONS.equals(lastPhaseId))
770             {
771                 updateModelValues(facesContext);
772                 continueProcess = true;
773             }
774             if (continueProcess || PhaseId.UPDATE_MODEL_VALUES.equals(lastPhaseId))
775             {
776                 invokeApplication(facesContext);
777                 continueProcess = true;
778             }
779             if (continueProcess || PhaseId.INVOKE_APPLICATION.equals(lastPhaseId))
780             {
781                 Integer step = (Integer) facesContext.getAttributes().get(LAST_RENDER_PHASE_STEP);
782                 if (step == null)
783                 {
784                     renderResponse(facesContext);
785                 }
786                 else
787                 {
788                     if (BEFORE_RENDER_STEP == step.intValue())
789                     {
790                         executeBuildViewCycle(facesContext);
791                         executeViewHandlerRender(facesContext);
792                         executeAfterRender(facesContext);
793                     }
794                     else if (BUILD_VIEW_CYCLE_STEP == step.intValue())
795                     {
796                         executeViewHandlerRender(facesContext);
797                         executeAfterRender(facesContext);
798                     }
799                     else if (VIEWHANDLER_RENDER_STEP == step.intValue())
800                     {
801                         executeAfterRender(facesContext);
802                     }
803                 }
804             }
805         }
806     }
807     
808     /**
809      * Indicate if annotation scanning should be done over the classpath. 
810      * By default it is set to false.
811      * 
812      * @return
813      */
814     protected boolean isScanAnnotations()
815     {
816         TestConfig testConfig = getTestJavaClass().getAnnotation(TestConfig.class);
817         if (testConfig != null)
818         {
819             return testConfig.scanAnnotations();
820         }
821         return false;
822     }
823     
824     public void executeBeforeRender(FacesContext facesContext)
825     {
826         if (lifecycle instanceof LifecycleImpl)
827         {
828             LifecycleImpl lifecycleImpl = (LifecycleImpl) lifecycle;
829             
830             Object phaseExecutor = null;
831             Object phaseManager = null;
832             try
833             {
834                 Field renderExecutorField = lifecycleImpl.getClass().getDeclaredField("renderExecutor");
835                 if (!renderExecutorField.isAccessible())
836                 {
837                     renderExecutorField.setAccessible(true);
838                 }
839                 phaseExecutor = renderExecutorField.get(lifecycleImpl);
840 
841                 if (facesContext.getResponseComplete())
842                 {
843                     return;
844                 }
845 
846                 phaseManager = facesContext.getAttributes().get(PHASE_MANAGER_INSTANCE);
847                 if (phaseManager == null)
848                 {
849                     Method getPhaseListenersMethod = lifecycleImpl.getClass().getDeclaredMethod("getPhaseListeners");
850                     if (!getPhaseListenersMethod.isAccessible())
851                     {
852                         getPhaseListenersMethod.setAccessible(true);
853                     }
854 
855                     Constructor<?> plmc = PHASE_MANAGER_CLASS.getDeclaredConstructor(
856                         new Class[]{Lifecycle.class, FacesContext.class, PhaseListener[].class});
857                     if (!plmc.isAccessible())
858                     {
859                         plmc.setAccessible(true);
860                     }
861                     phaseManager = plmc.newInstance(lifecycle, facesContext, getPhaseListenersMethod.invoke(
862                         lifecycleImpl, null));
863                     facesContext.getAttributes().put(PHASE_MANAGER_INSTANCE, phaseManager);
864                 }
865             }
866             catch (NoSuchFieldException ex)
867             {
868                 throw new IllegalStateException("Cannot get executors from LifecycleImpl", ex);
869             }
870             catch (SecurityException ex)
871             {
872                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
873             }
874             catch (IllegalArgumentException ex)
875             {
876                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
877             }
878             catch (IllegalAccessException ex)
879             {
880                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
881             }
882             catch (NoSuchMethodException ex)
883             {
884                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
885             }
886             catch (InvocationTargetException ex)
887             {
888                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
889             }
890             catch (InstantiationException ex)
891             {
892                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
893             }
894             
895             Flash flash = facesContext.getExternalContext().getFlash();
896             
897             try
898             {
899                 facesContext.setCurrentPhaseId(PhaseId.RENDER_RESPONSE);
900                 
901                 flash.doPrePhaseActions(facesContext);
902                 
903                 // let the PhaseExecutor do some pre-phase actions
904                 
905                 //renderExecutor.doPrePhaseActions(facesContext);
906                 Method doPrePhaseActionsMethod = phaseExecutor.getClass().getMethod(
907                     "doPrePhaseActions", FacesContext.class);
908                 if(!(doPrePhaseActionsMethod.isAccessible()))
909                 {
910                     doPrePhaseActionsMethod.setAccessible(true);
911                 }
912                 doPrePhaseActionsMethod.invoke(phaseExecutor, facesContext);
913                 
914                 //phaseListenerMgr.informPhaseListenersBefore(PhaseId.RENDER_RESPONSE);
915                 Method informPhaseListenersBeforeMethod = 
916                     phaseManager.getClass().getDeclaredMethod("informPhaseListenersBefore", PhaseId.class);
917                 if(!(informPhaseListenersBeforeMethod.isAccessible()))
918                 {
919                     informPhaseListenersBeforeMethod.setAccessible(true);
920                 }
921                 informPhaseListenersBeforeMethod.invoke(phaseManager, PhaseId.RENDER_RESPONSE);
922                 
923                 // also possible that one of the listeners completed the response
924                 if (facesContext.getResponseComplete())
925                 {
926                     return;
927                 }
928                 
929                 //renderExecutor.execute(facesContext);
930             }
931             
932             catch (Throwable e)
933             {
934                 // JSF 2.0: publish the executor's exception (if any).
935                 ExceptionQueuedEventContext context = new ExceptionQueuedEventContext (
936                     facesContext, e, null, PhaseId.RENDER_RESPONSE);
937                 facesContext.getApplication().publishEvent (facesContext, ExceptionQueuedEvent.class, context);
938             }
939             
940             finally
941             {
942                 /*
943                 phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
944                 flash.doPostPhaseActions(facesContext);
945                 
946                 // publish a field in the application map to indicate
947                 // that the first request has been processed
948                 requestProcessed(facesContext);
949                 */
950             }
951             
952             facesContext.getExceptionHandler().handle();
953             
954 
955             facesContext.getAttributes().remove(PHASE_MANAGER_INSTANCE);
956             
957             facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, BEFORE_RENDER_STEP);
958         }
959         else
960         {
961             throw new UnsupportedOperationException("Cannot execute phase on custom lifecycle instances");
962         }
963     }
964     
965     public void executeBuildViewCycle(FacesContext facesContext)
966     {
967         Application application = facesContext.getApplication();
968         ViewHandler viewHandler = application.getViewHandler();
969         UIViewRoot root;
970         UIViewRoot previousRoot;
971         String viewId;
972         String newViewId;
973         boolean isNotSameRoot;
974         int loops = 0;
975         int maxLoops = 15;
976         
977         if (facesContext.getViewRoot() == null)
978         {
979             throw new ViewNotFoundException("A view is required to execute "+facesContext.getCurrentPhaseId());
980         }
981         
982         try
983         {
984             // do-while, because the view might change in PreRenderViewEvent-listeners
985             do
986             {
987                 root = facesContext.getViewRoot();
988                 previousRoot = root;
989                 viewId = root.getViewId();
990                 
991                 ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(
992                         facesContext, viewId);
993                 if (vdl != null)
994                 {
995                     vdl.buildView(facesContext, root);
996                 }
997                 
998                 // publish a PreRenderViewEvent: note that the event listeners
999                 // of this event can change the view, so we have to perform the algorithm 
1000                 // until the viewId does not change when publishing this event.
1001                 application.publishEvent(facesContext, PreRenderViewEvent.class, root);
1002                 
1003                 // was the response marked as complete by an event listener?
1004                 if (facesContext.getResponseComplete())
1005                 {
1006                     return;
1007                 }
1008 
1009                 root = facesContext.getViewRoot();
1010                 
1011                 newViewId = root.getViewId();
1012                 
1013                 isNotSameRoot = !( (newViewId == null ? newViewId == viewId : newViewId.equals(viewId) ) && 
1014                         previousRoot.equals(root) ); 
1015                 
1016                 loops++;
1017             }
1018             while ((newViewId == null && viewId != null) 
1019                     || (newViewId != null && (!newViewId.equals(viewId) || isNotSameRoot ) ) && loops < maxLoops);
1020             
1021             if (loops == maxLoops)
1022             {
1023                 // PreRenderView reach maxLoops - probably a infinitive recursion:
1024                 boolean production = facesContext.isProjectStage(ProjectStage.Production);
1025                 /*
1026                 Level level = production ? Level.FINE : Level.WARNING;
1027                 if (log.isLoggable(level))
1028                 {
1029                     log.log(level, "Cicle over buildView-PreRenderViewEvent on RENDER_RESPONSE phase "
1030                                    + "reaches maximal limit, please check listeners for infinite recursion.");
1031                 }*/
1032             }
1033             
1034             facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, BUILD_VIEW_CYCLE_STEP);
1035         }
1036         catch (IOException e)
1037         {
1038             throw new FacesException(e.getMessage(), e);
1039         }
1040     }
1041     
1042     public void executeViewHandlerRender(FacesContext facesContext)
1043     {
1044         Application application = facesContext.getApplication();
1045         ViewHandler viewHandler = application.getViewHandler();
1046 
1047         try
1048         {
1049             viewHandler.renderView(facesContext, facesContext.getViewRoot());
1050             
1051             // log all unhandled FacesMessages, don't swallow them
1052             // perf: org.apache.myfaces.context.servlet.FacesContextImpl.getMessageList() creates
1053             // new Collections.unmodifiableList with every invocation->  call it only once
1054             // and messageList is RandomAccess -> use index based loop
1055             List<FacesMessage> messageList = facesContext.getMessageList();
1056             if (!messageList.isEmpty())
1057             {
1058                 StringBuilder builder = new StringBuilder();
1059                 //boolean shouldLog = false;
1060                 for (int i = 0, size = messageList.size(); i < size; i++)
1061                 {
1062                     FacesMessage message = messageList.get(i);
1063                     if (!message.isRendered())
1064                     {
1065                         builder.append("\n- ");
1066                         builder.append(message.getDetail());
1067                         
1068                         //shouldLog = true;
1069                     }
1070                 }
1071                 /*
1072                 if (shouldLog)
1073                 {
1074                     log.log(Level.WARNING, "There are some unhandled FacesMessages, " +
1075                             "this means not every FacesMessage had a chance to be rendered.\n" +
1076                             "These unhandled FacesMessages are: " + builder.toString());
1077                 }*/
1078             }
1079             facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, VIEWHANDLER_RENDER_STEP);
1080         }
1081         catch (IOException e)
1082         {
1083             throw new FacesException(e.getMessage(), e);
1084         }
1085     }
1086     
1087     public void executeAfterRender(FacesContext facesContext)
1088     {
1089         if (lifecycle instanceof LifecycleImpl)
1090         {
1091             LifecycleImpl lifecycleImpl = (LifecycleImpl) lifecycle;
1092             
1093             Object phaseExecutor = null;
1094             Object phaseManager = null;
1095             Method informPhaseListenersAfterMethod = null;
1096             try
1097             {
1098                 Field renderExecutorField = lifecycleImpl.getClass().getDeclaredField("renderExecutor");
1099                 if (!renderExecutorField.isAccessible())
1100                 {
1101                     renderExecutorField.setAccessible(true);
1102                 }
1103                 phaseExecutor = renderExecutorField.get(lifecycleImpl);
1104             
1105                 phaseManager = facesContext.getAttributes().get(PHASE_MANAGER_INSTANCE);
1106                 if (phaseManager == null)
1107                 {
1108                     Method getPhaseListenersMethod = lifecycleImpl.getClass().getDeclaredMethod("getPhaseListeners");
1109                     if (!getPhaseListenersMethod.isAccessible())
1110                     {
1111                         getPhaseListenersMethod.setAccessible(true);
1112                     }
1113 
1114                     Constructor<?> plmc = PHASE_MANAGER_CLASS.getDeclaredConstructor(
1115                         new Class[]{Lifecycle.class, FacesContext.class, PhaseListener[].class});
1116                     if (!plmc.isAccessible())
1117                     {
1118                         plmc.setAccessible(true);
1119                     }
1120                     phaseManager = plmc.newInstance(lifecycle, facesContext, 
1121                         getPhaseListenersMethod.invoke(lifecycleImpl, null));
1122                     facesContext.getAttributes().put(PHASE_MANAGER_INSTANCE, phaseManager);
1123                 }
1124 
1125                 //phaseListenerMgr.informPhaseListenersAfter(renderExecutor.getPhase());
1126                 informPhaseListenersAfterMethod = phaseManager.getClass().getDeclaredMethod(
1127                     "informPhaseListenersAfter", PhaseId.class);
1128                 if(!(informPhaseListenersAfterMethod.isAccessible()))
1129                 {
1130                     informPhaseListenersAfterMethod.setAccessible(true);
1131                 }
1132                 
1133                 informPhaseListenersAfterMethod.invoke(phaseManager, PhaseId.RENDER_RESPONSE);
1134             }
1135             catch (NoSuchFieldException ex)
1136             {
1137                 throw new IllegalStateException("Cannot get executors from LifecycleImpl", ex);
1138             }
1139             catch (SecurityException ex)
1140             {
1141                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1142             }
1143             catch (IllegalArgumentException ex)
1144             {
1145                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1146             }
1147             catch (IllegalAccessException ex)
1148             {
1149                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1150             }
1151             catch (NoSuchMethodException ex)
1152             {
1153                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1154             }
1155             catch (InvocationTargetException ex)
1156             {
1157                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1158             }
1159             catch (InstantiationException ex)
1160             {
1161                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1162             }
1163             
1164             Flash flash = facesContext.getExternalContext().getFlash();
1165             
1166             flash.doPostPhaseActions(facesContext);
1167             
1168             facesContext.getExceptionHandler().handle();
1169 
1170             facesContext.getAttributes().remove(PHASE_MANAGER_INSTANCE);
1171             
1172             facesContext.getAttributes().put(LAST_RENDER_PHASE_STEP, AFTER_RENDER_STEP);
1173             //End render response phase
1174             facesContext.getAttributes().put(LAST_PHASE_PROCESSED, PhaseId.RENDER_RESPONSE);
1175         }
1176         else
1177         {
1178             throw new UnsupportedOperationException("Cannot execute phase on custom lifecycle instances");
1179         }
1180     }
1181     
1182     /**
1183      * Execute an specified phase, doing some reflection over LifecycleImpl.
1184      * 
1185      * @param facesContext
1186      * @param phase
1187      * @throws Exception
1188      */
1189     protected void executePhase(FacesContext facesContext, PhaseId phase)
1190     {
1191         if (lifecycle instanceof LifecycleImpl)
1192         {
1193             LifecycleImpl lifecycleImpl = (LifecycleImpl) lifecycle;
1194             
1195             int phaseId = phase.equals(PhaseId.RESTORE_VIEW) ? 0 :
1196                           phase.equals(PhaseId.APPLY_REQUEST_VALUES) ? 1 : 
1197                           phase.equals(PhaseId.PROCESS_VALIDATIONS) ? 2 :
1198                           phase.equals(PhaseId.UPDATE_MODEL_VALUES) ? 3 : 
1199                           phase.equals(PhaseId.INVOKE_APPLICATION) ? 4 : 5 ;
1200             
1201             Method executePhaseMethod = null;
1202             Object phaseManager = null;
1203             Object phaseExecutor = null;
1204             try
1205             {
1206                 if (phaseId < 5)
1207                 {
1208                     Field lifecycleExecutorsField;
1209                         lifecycleExecutorsField = lifecycleImpl.getClass().getDeclaredField("lifecycleExecutors");
1210                         if (!lifecycleExecutorsField.isAccessible())
1211                         {
1212                             lifecycleExecutorsField.setAccessible(true);
1213                         }
1214                         phaseExecutor = ((Object[])lifecycleExecutorsField.get(lifecycleImpl))[phaseId];
1215                 }
1216                 else
1217                 {
1218                     Field renderExecutorField = lifecycleImpl.getClass().getDeclaredField("renderExecutor");
1219                     if (!renderExecutorField.isAccessible())
1220                     {
1221                         renderExecutorField.setAccessible(true);
1222                     }
1223                     phaseExecutor = renderExecutorField.get(lifecycleImpl);
1224                 }
1225 
1226                 phaseManager = facesContext.getAttributes().get(PHASE_MANAGER_INSTANCE);
1227                 if (phaseManager == null)
1228                 {
1229                     Method getPhaseListenersMethod = lifecycleImpl.getClass().getDeclaredMethod("getPhaseListeners");
1230                     if (!getPhaseListenersMethod.isAccessible())
1231                     {
1232                         getPhaseListenersMethod.setAccessible(true);
1233                     }
1234 
1235                     Constructor<?> plmc = PHASE_MANAGER_CLASS.getDeclaredConstructor(
1236                         new Class[]{Lifecycle.class, FacesContext.class, PhaseListener[].class});
1237                     if (!plmc.isAccessible())
1238                     {
1239                         plmc.setAccessible(true);
1240                     }
1241                     phaseManager = plmc.newInstance(lifecycle, facesContext, 
1242                         getPhaseListenersMethod.invoke(lifecycleImpl, null));
1243                     facesContext.getAttributes().put(PHASE_MANAGER_INSTANCE, phaseManager);
1244                 }
1245 
1246                 executePhaseMethod = lifecycleImpl.getClass().getDeclaredMethod("executePhase", new Class[]{
1247                         FacesContext.class, PHASE_EXECUTOR_CLASS, PHASE_MANAGER_CLASS});
1248                 if (!executePhaseMethod.isAccessible())
1249                 {
1250                     executePhaseMethod.setAccessible(true);
1251                 }
1252                 
1253                 executePhaseMethod.invoke(lifecycleImpl, facesContext, phaseExecutor, phaseManager);
1254             }
1255             catch (NoSuchFieldException ex)
1256             {
1257                 throw new IllegalStateException("Cannot get executors from LifecycleImpl", ex);
1258             }
1259             catch (SecurityException ex)
1260             {
1261                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1262             }
1263             catch (IllegalArgumentException ex)
1264             {
1265                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1266             }
1267             catch (IllegalAccessException ex)
1268             {
1269                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1270             }
1271             catch (NoSuchMethodException ex)
1272             {
1273                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1274             }
1275             catch (InvocationTargetException ex)
1276             {
1277                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1278             }
1279             catch (InstantiationException ex)
1280             {
1281                 throw new UnsupportedOperationException("Cannot get executors from LifecycleImpl", ex);
1282             }            
1283             
1284             if (phase.equals(PhaseId.RENDER_RESPONSE))
1285             {
1286                 facesContext.getAttributes().remove(PHASE_MANAGER_INSTANCE);
1287             }
1288         }
1289         else
1290         {
1291             throw new UnsupportedOperationException("Cannot execute phase on custom lifecycle instances");
1292         }
1293     }
1294     
1295     public String getRenderedContent(FacesContext facesContext) throws IOException
1296     {
1297         MockPrintWriter writer1 = (MockPrintWriter) (((HttpServletResponse) 
1298             facesContext.getExternalContext().getResponse()).getWriter());
1299         return String.valueOf(writer1.content());
1300     }
1301 
1302     public MockServletConfig getServletConfig()
1303     {
1304         return servletConfig;
1305     }
1306 
1307     public MockServletContext getServletContext()
1308     {
1309         return servletContext;
1310     }
1311 
1312     public InjectionProvider getInjectionProvider()
1313     {
1314         return injectionProvider;
1315     }
1316 
1317     // ------------------------------------------------------ Instance Variables
1318 
1319 
1320     // Thread context class loader saved and restored after each test
1321     private ClassLoader threadContextClassLoader = null;
1322     private boolean classLoaderSet = false;
1323     private Context jndiContext = null;
1324 
1325     // Servlet objects 
1326     protected MockServletConfig servletConfig = null;
1327     protected MockServletContext servletContext = null;
1328     protected MockWebContainer webContainer = null;
1329 
1330     // MyFaces specific objects created by the servlet environment
1331     protected StartupServletContextListener listener = null;
1332     protected FacesConfigurationProvider facesConfigurationProvider = null;
1333     private FacesInitializer facesInitializer = null;
1334     
1335     protected FacesContextFactory facesContextFactory = null;
1336     protected LifecycleFactory lifecycleFactory = null;
1337     protected Lifecycle lifecycle;
1338 
1339     private static FacesConfig standardFacesConfig;
1340     private static Map<String, SharedFacesConfiguration> sharedConfiguration =
1341         new ConcurrentHashMap<String, SharedFacesConfiguration>();
1342     private SharedFacesConfiguration jsfConfiguration;
1343     protected TestClass testClass;
1344     protected Object testInstance;
1345 
1346     //protected WebBeansConfigurationListener owbListener;
1347     protected InjectionProvider injectionProvider;
1348 
1349     /**
1350      * @return the facesInitializer
1351      */
1352     protected FacesInitializer getFacesInitializer()
1353     {
1354         if (facesInitializer == null)
1355         {
1356             facesInitializer = createFacesInitializer();
1357         }
1358         return facesInitializer;
1359     }
1360 
1361     /**
1362      * @param facesInitializer the facesInitializer to set
1363      */
1364     protected void setFacesInitializer(FacesInitializer facesInitializer)
1365     {
1366         this.facesInitializer = facesInitializer;
1367     }
1368     
1369     protected Class<?> getTestJavaClass()
1370     {
1371         return testClass.getJavaClass();
1372     }
1373     
1374 
1375     // ------------------------------------------------------ Subclasses
1376 
1377     /**
1378      * Mock FacesConfigurationProvider that replace the original ViewDeclarationLanguageFactory
1379      * with a customized one that contains only facelets vdl and cache some FacesConfig that 
1380      * does not change to reduce the time required to process each test.
1381      * 
1382      * @author Leonardo Uribe
1383      *
1384      */
1385     protected class MyFacesMockFacesConfigurationProvider extends DefaultFacesConfigurationProvider
1386     {
1387         
1388         public MyFacesMockFacesConfigurationProvider()
1389         {
1390         }
1391         
1392         @Override
1393         public FacesConfig getStandardFacesConfig(ExternalContext ectx)
1394         {
1395             if (standardFacesConfig == null)
1396             {
1397                 FacesConfig sfc = super.getStandardFacesConfig(ectx);
1398                 FactoryImpl factory = (FactoryImpl) sfc.getFactories().get(0);
1399                 // Override the default vdl factory with a mock one that only load
1400                 // facelet views
1401                 factory.getViewDeclarationLanguageFactory().set(0, 
1402                     MockMyFacesViewDeclarationLanguageFactory.class.getName());
1403                 standardFacesConfig = sfc;
1404             }
1405             return standardFacesConfig;
1406         }
1407 
1408         @Override
1409         public FacesConfig getAnnotationsFacesConfig(ExternalContext ectx,
1410                 boolean metadataComplete)
1411         {
1412             FacesConfig facesConfig = jsfConfiguration.getAnnotationsFacesConfig();
1413             if (facesConfig == null)
1414             {
1415                 if (isScanAnnotations())
1416                 {
1417                     facesConfig = super.getAnnotationsFacesConfig(ectx, metadataComplete); 
1418                 }
1419 
1420                 ManagedBeans annoManagedBeans = getTestJavaClass().getAnnotation(ManagedBeans.class);
1421                 if (annoManagedBeans != null)
1422                 {
1423                     if (facesConfig == null)
1424                     {
1425                         facesConfig = new org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl();
1426                     }
1427                     for (PageBean annoPageBean : annoManagedBeans.value())
1428                     {
1429                         org.apache.myfaces.config.impl.digester.elements.ManagedBeanImpl bean = new 
1430                             org.apache.myfaces.config.impl.digester.elements.ManagedBeanImpl();
1431                         bean.setBeanClass(annoPageBean.clazz().getName());
1432                         bean.setName(annoPageBean.name() == null ? annoPageBean.clazz().getName() : 
1433                             annoPageBean.name());
1434                         bean.setScope(annoPageBean.scope() == null ? "request" : annoPageBean.scope());
1435                         bean.setEager(Boolean.toString(annoPageBean.eager()));
1436 
1437                         ((org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl)facesConfig).
1438                             addManagedBean(bean);
1439                     }
1440                 }
1441 
1442                 PageBean annoPageBean = getTestJavaClass().getAnnotation(PageBean.class);
1443                 if (annoPageBean != null)
1444                 {
1445                     if (facesConfig == null)
1446                     {
1447                         facesConfig = new org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl();
1448                     }
1449                     org.apache.myfaces.config.impl.digester.elements.ManagedBeanImpl bean = new 
1450                         org.apache.myfaces.config.impl.digester.elements.ManagedBeanImpl();
1451                     bean.setBeanClass(annoPageBean.clazz().getName());
1452                     bean.setName(annoPageBean.name() == null ? annoPageBean.clazz().getName() : annoPageBean.name());
1453                     bean.setScope(annoPageBean.scope() == null ? "request" : annoPageBean.scope());
1454                     bean.setEager(Boolean.toString(annoPageBean.eager()));
1455 
1456                     ((org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl)facesConfig).
1457                         addManagedBean(bean);
1458                 }
1459                 jsfConfiguration.setAnnotationFacesConfig(facesConfig);
1460             }
1461             return facesConfig;
1462         }
1463 
1464         @Override
1465         public List<FacesConfig> getClassloaderFacesConfig(ExternalContext ectx)
1466         {
1467             List<FacesConfig> list = jsfConfiguration.getClassloaderFacesConfig();
1468             if (list == null)
1469             {
1470                 list = super.getClassloaderFacesConfig(ectx);
1471                 jsfConfiguration.setClassloaderFacesConfig(list);
1472             }
1473             return list;
1474         }
1475 
1476         @Override
1477         public List<FacesConfig> getFaceletTaglibFacesConfig(ExternalContext externalContext)
1478         {
1479             List<FacesConfig> list = jsfConfiguration.getFaceletTaglibFacesConfig();
1480             if (list == null)
1481             {
1482                 list = super.getFaceletTaglibFacesConfig(externalContext);
1483                 jsfConfiguration.setFaceletTaglibFacesConfig(list);
1484             }
1485             return list;
1486         }
1487 
1488         @Override
1489         public List<FacesConfig> getFacesFlowFacesConfig(ExternalContext ectx)
1490         {
1491             List<FacesConfig> list = jsfConfiguration.getFacesFlowFacesConfig();
1492             if (list == null)
1493             {
1494                 list = super.getFacesFlowFacesConfig(ectx);
1495                 jsfConfiguration.setFacesFlowFacesConfig(list);
1496             }
1497             return list;
1498         }
1499 
1500         @Override
1501         public FacesConfig getMetaInfServicesFacesConfig(ExternalContext ectx)
1502         {
1503             FacesConfig facesConfig = jsfConfiguration.getMetaInfServicesFacesConfig();
1504             if (facesConfig == null)
1505             {
1506                 facesConfig = super.getMetaInfServicesFacesConfig(ectx);
1507             }
1508             return facesConfig;
1509         }
1510         
1511         @Override
1512         public List<FacesConfig> getContextSpecifiedFacesConfig(ExternalContext ectx)
1513         {
1514             List<FacesConfig> appConfigResources = super.getContextSpecifiedFacesConfig(ectx);
1515             
1516             DeclareFacesConfig annoFacesConfig = getTestJavaClass().getAnnotation(DeclareFacesConfig.class);
1517             if (annoFacesConfig != null)
1518             {
1519                 Logger log = Logger.getLogger(getTestJavaClass().getName());
1520                 try
1521                 {
1522                     for (String systemId : annoFacesConfig.value())
1523                     {
1524                         if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
1525                         {
1526                             URL url = ectx.getResource(systemId);
1527                             if (url != null)
1528                             {
1529                                 validateFacesConfig(ectx, url);
1530                             }
1531                         }   
1532                         InputStream stream = ectx.getResourceAsStream(systemId);
1533                         if (stream == null)
1534                         {
1535                             
1536                             log.severe("Faces config resource " + systemId + " not found");
1537                             continue;
1538                         }
1539             
1540                         if (log.isLoggable(Level.INFO))
1541                         {
1542                             log.info("Reading config " + systemId);
1543                         }
1544                         appConfigResources.add(getUnmarshaller(ectx).getFacesConfig(stream, systemId));
1545                         //getDispenser().feed(getUnmarshaller().getFacesConfig(stream, systemId));
1546                         stream.close();
1547     
1548                     }
1549                 }
1550                 catch (Throwable e)
1551                 {
1552                     throw new FacesException(e);
1553                 }
1554             }
1555             return appConfigResources;
1556         }
1557     }
1558     
1559     private void validateFacesConfig(ExternalContext ectx, URL url) throws IOException, SAXException
1560     {
1561         String version = ConfigFilesXmlValidationUtils.getFacesConfigVersion(url);
1562         if ("1.2".equals(version) || "2.0".equals(version) || "2.1".equals(version))
1563         {
1564             ConfigFilesXmlValidationUtils.validateFacesConfigFile(url, ectx, version);
1565         }
1566     }
1567     
1568     protected class JUnitFacesInitializer extends AbstractFacesInitializer
1569     {
1570         private final AbstractJsfTestContainer testCase;
1571         
1572         public JUnitFacesInitializer(AbstractJsfTestContainer testCase)
1573         {
1574             this.testCase = testCase;
1575         }
1576         
1577         @Override
1578         protected void initContainerIntegration(ServletContext servletContext,
1579                 ExternalContext externalContext)
1580         {
1581             if (servletContext.getInitParameter("org.apache.myfaces.spi.InjectionProvider") == null)
1582             {
1583                 if (ExternalSpecifications.isCDIAvailable(externalContext))
1584                 {
1585                     ((MockServletContext)servletContext).addInitParameter("org.apache.myfaces.spi.InjectionProvider", 
1586                         CDIAnnotationDelegateInjectionProvider.class.getName());
1587                 }
1588                 else
1589                 {
1590                     ((MockServletContext)servletContext).addInitParameter("org.apache.myfaces.spi.InjectionProvider", 
1591                         NoInjectionAnnotationInjectionProvider.class.getName());
1592                 }
1593             }
1594             
1595             ExpressionFactory expressionFactory = createExpressionFactory();
1596 
1597             RuntimeConfig runtimeConfig = buildConfiguration(servletContext, externalContext, expressionFactory);
1598         }
1599 
1600         public AbstractJsfTestContainer getTestCase()
1601         {
1602             return testCase;
1603         }
1604 
1605     }
1606     
1607     protected static class SharedFacesConfiguration
1608     {
1609         private List<FacesConfig> classloaderFacesConfig;
1610         private FacesConfig annotationFacesConfig;
1611         private List<FacesConfig> faceletTaglibFacesConfig;
1612         private List<FacesConfig> facesFlowFacesConfig;
1613         private FacesConfig metaInfServicesFacesConfig;
1614         private List<FacesConfig> contextSpecifiedFacesConfig;
1615 
1616         /**
1617          * @return the annotationFacesConfig
1618          */
1619         public FacesConfig getAnnotationsFacesConfig()
1620         {
1621             return annotationFacesConfig;
1622         }
1623 
1624         /**
1625          * @param annotationFacesConfig the annotationFacesConfig to set
1626          */
1627         public void setAnnotationFacesConfig(FacesConfig annotationFacesConfig)
1628         {
1629             this.annotationFacesConfig = annotationFacesConfig;
1630         }
1631 
1632         /**
1633          * @return the annotationFacesConfig
1634          */
1635         public FacesConfig getAnnotationFacesConfig()
1636         {
1637             return annotationFacesConfig;
1638         }
1639 
1640         /**
1641          * @return the faceletTaglibFacesConfig
1642          */
1643         public List<FacesConfig> getFaceletTaglibFacesConfig()
1644         {
1645             return faceletTaglibFacesConfig;
1646         }
1647 
1648         /**
1649          * @param faceletTaglibFacesConfig the faceletTaglibFacesConfig to set
1650          */
1651         public void setFaceletTaglibFacesConfig(List<FacesConfig> faceletTaglibFacesConfig)
1652         {
1653             this.faceletTaglibFacesConfig = faceletTaglibFacesConfig;
1654         }
1655 
1656         /**
1657          * @return the facesFlowFacesConfig
1658          */
1659         public List<FacesConfig> getFacesFlowFacesConfig()
1660         {
1661             return facesFlowFacesConfig;
1662         }
1663 
1664         /**
1665          * @param facesFlowFacesConfig the facesFlowFacesConfig to set
1666          */
1667         public void setFacesFlowFacesConfig(List<FacesConfig> facesFlowFacesConfig)
1668         {
1669             this.facesFlowFacesConfig = facesFlowFacesConfig;
1670         }
1671 
1672         /**
1673          * @return the metaInfServicesFacesConfig
1674          */
1675         public FacesConfig getMetaInfServicesFacesConfig()
1676         {
1677             return metaInfServicesFacesConfig;
1678         }
1679 
1680         /**
1681          * @param metaInfServicesFacesConfig the metaInfServicesFacesConfig to set
1682          */
1683         public void setMetaInfServicesFacesConfig(FacesConfig metaInfServicesFacesConfig)
1684         {
1685             this.metaInfServicesFacesConfig = metaInfServicesFacesConfig;
1686         }
1687 
1688         /**
1689          * @return the contextSpecifiedFacesConfig
1690          */
1691         public List<FacesConfig> getContextSpecifiedFacesConfig()
1692         {
1693             return contextSpecifiedFacesConfig;
1694         }
1695 
1696         /**
1697          * @param contextSpecifiedFacesConfig the contextSpecifiedFacesConfig to set
1698          */
1699         public void setContextSpecifiedFacesConfig(List<FacesConfig> contextSpecifiedFacesConfig)
1700         {
1701             this.contextSpecifiedFacesConfig = contextSpecifiedFacesConfig;
1702         }
1703 
1704         /**
1705          * @return the classloaderFacesConfigList
1706          */
1707         public List<FacesConfig> getClassloaderFacesConfig()
1708         {
1709             return classloaderFacesConfig;
1710         }
1711 
1712         /**
1713          * @param classloaderFacesConfigList the classloaderFacesConfigList to set
1714          */
1715         public void setClassloaderFacesConfig(List<FacesConfig> classloaderFacesConfigList)
1716         {
1717             this.classloaderFacesConfig = classloaderFacesConfigList;
1718         }
1719     }
1720 }