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