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.config;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.FileNotFoundException;
23  import org.apache.myfaces.config.annotation.AnnotationConfigurator;
24  import org.apache.myfaces.config.element.FacesConfig;
25  import org.apache.myfaces.config.impl.digester.DigesterFacesConfigUnmarshallerImpl;
26  import org.apache.myfaces.shared.config.MyfacesConfig;
27  import org.apache.myfaces.shared.util.ClassUtils;
28  import org.apache.myfaces.spi.FacesConfigResourceProvider;
29  import org.apache.myfaces.spi.FacesConfigResourceProviderFactory;
30  import org.apache.myfaces.spi.FacesConfigurationProvider;
31  import org.apache.myfaces.spi.ServiceProviderFinderFactory;
32  import org.xml.sax.SAXException;
33  
34  import javax.faces.FacesException;
35  import javax.faces.FactoryFinder;
36  import javax.faces.context.ExternalContext;
37  import javax.faces.webapp.FacesServlet;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.io.PushbackInputStream;
41  import java.io.StringReader;
42  import java.net.MalformedURLException;
43  import java.net.URL;
44  import java.net.URLConnection;
45  import java.util.ArrayList;
46  import java.util.Collection;
47  import java.util.Collections;
48  import java.util.HashSet;
49  import java.util.List;
50  import java.util.ServiceLoader;
51  import java.util.Set;
52  import java.util.StringTokenizer;
53  import java.util.logging.Level;
54  import java.util.logging.Logger;
55  import javax.faces.application.ApplicationConfigurationPopulator;
56  import javax.faces.application.ViewHandler;
57  import javax.xml.parsers.DocumentBuilder;
58  import javax.xml.parsers.DocumentBuilderFactory;
59  import javax.xml.parsers.ParserConfigurationException;
60  import javax.xml.transform.OutputKeys;
61  import javax.xml.transform.Transformer;
62  import javax.xml.transform.TransformerConfigurationException;
63  import javax.xml.transform.TransformerException;
64  import javax.xml.transform.TransformerFactory;
65  import javax.xml.transform.dom.DOMSource;
66  import javax.xml.transform.stream.StreamResult;
67  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
68  import org.apache.myfaces.config.element.FacesFlowDefinition;
69  import org.apache.myfaces.config.element.facelets.FaceletTagLibrary;
70  import org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl;
71  import org.apache.myfaces.config.impl.digester.elements.FacesFlowDefinitionImpl;
72  import org.apache.myfaces.config.impl.digester.elements.FacesFlowReturnImpl;
73  import org.apache.myfaces.config.impl.digester.elements.NavigationCaseImpl;
74  import org.apache.myfaces.shared.util.FastWriter;
75  import org.apache.myfaces.shared.util.WebConfigParamUtils;
76  import org.apache.myfaces.spi.FaceletConfigResourceProvider;
77  import org.apache.myfaces.spi.FaceletConfigResourceProviderFactory;
78  import org.apache.myfaces.spi.ServiceProviderFinder;
79  import org.apache.myfaces.view.facelets.compiler.TagLibraryConfigUnmarshallerImpl;
80  import org.w3c.dom.DOMImplementation;
81  import org.w3c.dom.Document;
82  
83  /**
84   * 
85   * @author Leonardo Uribe
86   * @since 2.0.3
87   */
88  public class DefaultFacesConfigurationProvider extends FacesConfigurationProvider
89  {
90  
91      private static final String STANDARD_FACES_CONFIG_RESOURCE = "META-INF/standard-faces-config.xml";
92      
93      //private static final String META_INF_SERVICES_RESOURCE_PREFIX = "META-INF/services/";
94  
95      private static final String DEFAULT_FACES_CONFIG = "/WEB-INF/faces-config.xml";
96  
97      private static final Set<String> FACTORY_NAMES = new HashSet<String>();
98      static
99      {
100         FACTORY_NAMES.add(FactoryFinder.APPLICATION_FACTORY);
101         FACTORY_NAMES.add(FactoryFinder.CLIENT_WINDOW_FACTORY);
102         FACTORY_NAMES.add(FactoryFinder.EXCEPTION_HANDLER_FACTORY);
103         FACTORY_NAMES.add(FactoryFinder.EXTERNAL_CONTEXT_FACTORY);
104         FACTORY_NAMES.add(FactoryFinder.FACELET_CACHE_FACTORY);
105         FACTORY_NAMES.add(FactoryFinder.FACES_CONTEXT_FACTORY);
106         FACTORY_NAMES.add(FactoryFinder.FLASH_FACTORY);
107         FACTORY_NAMES.add(FactoryFinder.FLOW_HANDLER_FACTORY);
108         FACTORY_NAMES.add(FactoryFinder.LIFECYCLE_FACTORY);
109         FACTORY_NAMES.add(FactoryFinder.RENDER_KIT_FACTORY);
110         FACTORY_NAMES.add(FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY);
111         FACTORY_NAMES.add(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY);
112         FACTORY_NAMES.add(FactoryFinder.VISIT_CONTEXT_FACTORY);
113         FACTORY_NAMES.add(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
114         FACTORY_NAMES.add(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY);
115     }
116     
117     /**
118      * Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.
119      */
120     @JSFWebConfigParam(since = "2.0",
121             desc = "Set of .taglib.xml files, separated by ';' that should be loaded by facelet engine.",
122             deprecated = true)
123     private final static String PARAM_LIBRARIES_DEPRECATED = "facelets.LIBRARIES";
124 
125     private final static String[] PARAMS_LIBRARIES = {ViewHandler.FACELETS_LIBRARIES_PARAM_NAME,
126         PARAM_LIBRARIES_DEPRECATED};
127 
128     private static final Logger log = Logger.getLogger(DefaultFacesConfigurationProvider.class.getName());
129 
130     private FacesConfigUnmarshaller<? extends FacesConfig> _unmarshaller;
131     
132     private AnnotationConfigurator _annotationConfigurator;
133 
134     protected void setUnmarshaller(ExternalContext ectx, FacesConfigUnmarshaller<? extends FacesConfig> unmarshaller)
135     {
136         _unmarshaller = unmarshaller;
137     }
138 
139     @SuppressWarnings("unchecked")
140     protected FacesConfigUnmarshaller<? extends FacesConfig> getUnmarshaller(ExternalContext ectx)
141     {
142         if (_unmarshaller == null)
143         {
144             _unmarshaller = new DigesterFacesConfigUnmarshallerImpl(ectx);
145         }
146         return _unmarshaller;
147     }
148     
149     protected void setAnnotationConfigurator(AnnotationConfigurator configurator)
150     {
151         _annotationConfigurator = configurator;
152     }
153     
154     protected AnnotationConfigurator getAnnotationConfigurator()
155     {
156         if (_annotationConfigurator == null)
157         {
158             _annotationConfigurator = new AnnotationConfigurator();
159         }
160         return _annotationConfigurator;
161     }
162 
163     @Override
164     public FacesConfig getStandardFacesConfig(ExternalContext ectx)
165     {
166         try
167         {
168             if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
169             {
170                 URL url = ClassUtils.getResource(STANDARD_FACES_CONFIG_RESOURCE);
171                 if (url != null)
172                 {
173                     validateFacesConfig(ectx, url);
174                 }
175             }
176             InputStream stream = ClassUtils.getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE);
177             if (stream == null)
178             {
179                 throw new FacesException("Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found");
180             }
181             if (log.isLoggable(Level.INFO))
182             {
183                 log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE);
184             }
185             
186             FacesConfig facesConfig = getUnmarshaller(ectx).getFacesConfig(stream, STANDARD_FACES_CONFIG_RESOURCE);
187             stream.close();
188             return facesConfig;
189         }
190         catch (IOException e)
191         {
192             throw new FacesException(e);
193         }
194         catch (SAXException e)
195         {
196             throw new FacesException(e);
197         }
198     }
199 
200     @Override
201     public FacesConfig getAnnotationsFacesConfig(ExternalContext ectx, boolean metadataComplete)
202     {
203         return getAnnotationConfigurator().createFacesConfig(ectx, metadataComplete);
204     }
205 
206     /**
207      * This method performs part of the factory search outlined in section 10.2.6.1.
208      */
209     @Override
210     public FacesConfig getMetaInfServicesFacesConfig(ExternalContext ectx)
211     {
212         try
213         {
214             org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl facesConfig
215                     = new org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl();
216             org.apache.myfaces.config.impl.digester.elements.FactoryImpl factory
217                     = new org.apache.myfaces.config.impl.digester.elements.FactoryImpl();
218             
219             facesConfig.addFactory(factory);
220             
221             for (String factoryName : FACTORY_NAMES)
222             {
223                 List<String> classList = ServiceProviderFinderFactory.getServiceProviderFinder(ectx)
224                         .getServiceProviderList(factoryName);
225                 
226                 for (String className : classList)
227                 {
228                     if (log.isLoggable(Level.INFO))
229                     {
230                         log.info("Found " + factoryName + " factory implementation: " + className);
231                     }
232 
233                     if (factoryName.equals(FactoryFinder.APPLICATION_FACTORY))
234                     {
235                         factory.addApplicationFactory(className);
236                     } 
237                     else if(factoryName.equals(FactoryFinder.EXCEPTION_HANDLER_FACTORY)) 
238                     {
239                         factory.addExceptionHandlerFactory(className);
240                     } 
241                     else if (factoryName.equals(FactoryFinder.EXTERNAL_CONTEXT_FACTORY))
242                     {
243                         factory.addExternalContextFactory(className);
244                     } 
245                     else if (factoryName.equals(FactoryFinder.FACES_CONTEXT_FACTORY))
246                     {
247                         factory.addFacesContextFactory(className);
248                     } 
249                     else if (factoryName.equals(FactoryFinder.LIFECYCLE_FACTORY))
250                     {
251                         factory.addLifecycleFactory(className);
252                     } 
253                     else if (factoryName.equals(FactoryFinder.RENDER_KIT_FACTORY))
254                     {
255                         factory.addRenderkitFactory(className);
256                     } 
257                     else if(factoryName.equals(FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY)) 
258                     {
259                         factory.addTagHandlerDelegateFactory(className);
260                     } 
261                     else if (factoryName.equals(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY))
262                     {
263                         factory.addPartialViewContextFactory(className);
264                     } 
265                     else if(factoryName.equals(FactoryFinder.VISIT_CONTEXT_FACTORY)) 
266                     {
267                         factory.addVisitContextFactory(className);
268                     } 
269                     else if(factoryName.equals(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY)) 
270                     {
271                         factory.addViewDeclarationLanguageFactory(className);
272                     }
273                     else if(factoryName.equals(FactoryFinder.FLASH_FACTORY)) 
274                     {
275                         factory.addFlashFactory(className);
276                     }
277                     else if(factoryName.equals(FactoryFinder.FLOW_HANDLER_FACTORY)) 
278                     {
279                         factory.addFlowHandlerFactory(className);
280                     }
281                     else if(factoryName.equals(FactoryFinder.CLIENT_WINDOW_FACTORY)) 
282                     {
283                         factory.addClientWindowFactory(className);
284                     }
285                     else if(factoryName.equals(FactoryFinder.FACELET_CACHE_FACTORY)) 
286                     {
287                         factory.addFaceletCacheFactory(className);
288                     }
289                     else if(factoryName.equals(FactoryFinder.SEARCH_EXPRESSION_CONTEXT_FACTORY)) 
290                     {
291                         factory.addSearchExpressionContextFactory(className);
292                     }
293                     else
294                     {
295                         throw new IllegalStateException("Unexpected factory name " + factoryName);
296                     }
297                 }
298             }
299             return facesConfig;
300         }
301         catch (Throwable e)
302         {
303             throw new FacesException(e);
304         }
305     }
306 
307     /**
308      * This method fixes MYFACES-208
309      */
310     @Override
311     public List<FacesConfig> getClassloaderFacesConfig(ExternalContext ectx)
312     {
313         List<FacesConfig> appConfigResources = new ArrayList<FacesConfig>();
314         try
315         {
316             FacesConfigResourceProvider provider = FacesConfigResourceProviderFactory.
317                 getFacesConfigResourceProviderFactory(ectx).createFacesConfigResourceProvider(ectx);
318             
319             Collection<URL> facesConfigs = provider.getMetaInfConfigurationResources(ectx);
320             
321             for (URL url : facesConfigs)
322             {
323                 if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
324                 {
325                     validateFacesConfig(ectx, url);
326                 }
327                 InputStream stream = null;
328                 try
329                 {
330                     stream = openStreamWithoutCache(url);
331                     if (log.isLoggable(Level.INFO))
332                     {
333                         log.info("Reading config : " + url.toExternalForm());
334                     }
335                     appConfigResources.add(getUnmarshaller(ectx).getFacesConfig(stream, url.toExternalForm()));
336                     //getDispenser().feed(getUnmarshaller().getFacesConfig(stream, entry.getKey()));
337                 }
338                 finally
339                 {
340                     if (stream != null)
341                     {
342                         stream.close();
343                     }
344                 }
345             }
346         }
347         catch (Throwable e)
348         {
349             throw new FacesException(e);
350         }
351         return appConfigResources;
352     }
353 
354     @Override
355     public List<FacesConfig> getContextSpecifiedFacesConfig(ExternalContext ectx)
356     {
357         List<FacesConfig> appConfigResources = new ArrayList<FacesConfig>();
358         try
359         {
360             for (String systemId : getConfigFilesList(ectx))
361             {
362                 if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
363                 {
364                     URL url = ectx.getResource(systemId);
365                     if (url != null)
366                     {
367                         validateFacesConfig(ectx, url);
368                     }
369                 }            
370                 InputStream stream = ectx.getResourceAsStream(systemId);
371                 if (stream == null)
372                 {
373                     log.severe("Faces config resource " + systemId + " not found");
374                     continue;
375                 }
376     
377                 if (log.isLoggable(Level.INFO))
378                 {
379                     log.info("Reading config " + systemId);
380                 }
381                 appConfigResources.add(getUnmarshaller(ectx).getFacesConfig(stream, systemId));
382                 //getDispenser().feed(getUnmarshaller().getFacesConfig(stream, systemId));
383                 stream.close();
384             }
385         }
386         catch (Throwable e)
387         {
388             throw new FacesException(e);
389         }
390         return appConfigResources;
391     }
392     
393     @Override
394     public FacesConfig getWebAppFacesConfig(ExternalContext ectx)
395     {
396         try
397         {
398             // skip parsing/validating a empty faces-config.xml to avoid warn/error logs
399             try (InputStream stream = ectx.getResourceAsStream(DEFAULT_FACES_CONFIG))
400             {
401                 if (stream != null)
402                 {
403                     ByteArrayOutputStream out = new ByteArrayOutputStream();
404                     byte[] buffer = new byte[1024];
405                     int length;
406                     while ((length = stream.read(buffer)) != -1)
407                     {
408                         out.write(buffer, 0, length);
409                     }
410                     out.flush();
411 
412                     String content = new String(out.toByteArray());
413                     if (content.trim().isEmpty())
414                     {
415                         return new FacesConfigImpl();
416                     }
417                 }
418             }
419 
420             FacesConfig webAppConfig = null;
421             // web application config
422             if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
423             {
424                 URL url = ectx.getResource(DEFAULT_FACES_CONFIG);
425                 if (url != null)
426                 {
427                     validateFacesConfig(ectx, url);
428                 }
429             }
430             InputStream stream = ectx.getResourceAsStream(DEFAULT_FACES_CONFIG);
431             if (stream != null)
432             {
433                 if (log.isLoggable(Level.INFO))
434                 {
435                     log.info("Reading config /WEB-INF/faces-config.xml");
436                 }
437                 webAppConfig = getUnmarshaller(ectx).getFacesConfig(stream, DEFAULT_FACES_CONFIG);
438                 //getDispenser().feed(getUnmarshaller().getFacesConfig(stream, DEFAULT_FACES_CONFIG));
439                 stream.close();
440             }
441             return webAppConfig;
442         }
443         catch (IOException e)
444         {
445             throw new FacesException(e);
446         }
447         catch (SAXException e)
448         {
449             throw new FacesException(e);
450         }
451 
452     }
453 
454     private InputStream openStreamWithoutCache(URL url) throws IOException
455     {
456         URLConnection connection = url.openConnection();
457         connection.setUseCaches(false);
458         return connection.getInputStream();
459     }
460 
461     private List<String> getConfigFilesList(ExternalContext ectx)
462     {
463         String configFiles = ectx.getInitParameter(FacesServlet.CONFIG_FILES_ATTR);
464         List<String> configFilesList = new ArrayList<String>();
465         if (configFiles != null)
466         {
467             StringTokenizer st = new StringTokenizer(configFiles, ",", false);
468             while (st.hasMoreTokens())
469             {
470                 String systemId = st.nextToken().trim();
471 
472                 if (DEFAULT_FACES_CONFIG.equals(systemId))
473                 {
474                     if (log.isLoggable(Level.WARNING))
475                     {
476                         log.warning(DEFAULT_FACES_CONFIG + " has been specified in the "
477                                 + FacesServlet.CONFIG_FILES_ATTR
478                                 + " context parameter of "
479                                 + "the deployment descriptor. This will automatically be removed, "
480                                 + "if we wouldn't do this, it would be loaded twice.  See JSF spec 1.1, 10.3.2");
481                     }
482                 }
483                 else
484                 {
485                     configFilesList.add(systemId);
486                 }
487             }
488         }
489         return configFilesList;
490     }
491     
492     private void validateFacesConfig(ExternalContext ectx, URL url) throws IOException, SAXException
493     {
494         String version = ConfigFilesXmlValidationUtils.getFacesConfigVersion(url);
495         if ("1.2".equals(version) || "2.0".equals(version) || "2.1".equals(version) 
496             || "2.2".equals(version) || "2.3".equals(version))
497         {
498             ConfigFilesXmlValidationUtils.validateFacesConfigFile(url, ectx, version);
499         }
500     }
501 
502     @Override
503     public List<FacesConfig> getApplicationConfigurationResourceDocumentPopulatorFacesConfig(ExternalContext ectx)
504     {
505         ServiceProviderFinder spff = ServiceProviderFinderFactory.getServiceProviderFinder(ectx);
506         ServiceLoader<ApplicationConfigurationPopulator> instances = 
507             spff.load(ApplicationConfigurationPopulator.class);
508         if (instances != null)
509         {
510             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
511             // namespace aware
512             factory.setNamespaceAware(true);
513             // no validation
514             factory.setValidating(false);
515             
516             DocumentBuilder builder = null;
517             DOMImplementation domImpl = null;
518             try
519             {
520                 builder = factory.newDocumentBuilder();
521                 domImpl = builder.getDOMImplementation();
522             }
523             catch (ParserConfigurationException ex)
524             {
525                 log.log(Level.SEVERE, "Cannot create dom document builder, skipping it", ex);
526             }
527             
528             if (builder != null)
529             {
530                 List<FacesConfig> facesConfigList = new ArrayList<FacesConfig>();
531                 List<Document> documentList = new ArrayList<Document>();
532                 for (ApplicationConfigurationPopulator populator : instances)
533                 {
534                     // Spec says "... For each implementation, create a fresh org.w3c.dom.Document 
535                     // instance, configured to be in the XML namespace of the
536                     // application configuration resource format. ..."
537                     Document document = domImpl.createDocument(
538                         "http://java.sun.com/xml/ns/javaee", "faces-config", null);
539                     //Document document = builder.newDocument();
540                     populator.populateApplicationConfiguration(document);
541                     documentList.add(document);
542                 }
543                 
544                 // Parse document. This strategy construct the faces-config.xml in a
545                 // memory buffer and then loads it using commons digester.
546                 // TODO: Find a better way without write the DOM and read it again and without
547                 // rewrite commons-digester parser!.
548                 Transformer trans = null;
549                 try
550                 {
551                     trans = TransformerFactory.newInstance().newTransformer();
552                     trans.setOutputProperty(OutputKeys.INDENT, "no");
553                     trans.setOutputProperty(OutputKeys.METHOD, "xml");
554                     trans.setOutputProperty(OutputKeys.VERSION, "1.0");
555                     trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
556                 }
557                 catch (TransformerConfigurationException ex)
558                 {
559                     Logger.getLogger(DefaultFacesConfigurationProvider.class.getName()).log(Level.SEVERE, null, ex);
560                 }
561 
562                 if (trans != null)
563                 {
564                     FastWriter xmlAsWriter = new FastWriter();
565                     for (int i = 0; i < documentList.size(); i++)
566                     {
567                         Document document = documentList.get(i);
568                         xmlAsWriter.reset();
569                         try
570                         {
571                             DOMSource source = new DOMSource(document);
572                             StreamResult result = new StreamResult(xmlAsWriter);
573 
574                             trans.transform(source, result);
575 
576                             StringReader xmlReader = new StringReader(xmlAsWriter.toString());
577                             FacesConfig facesConfig = getUnmarshaller(ectx).getFacesConfig(
578                                 xmlReader);
579                             facesConfigList.add(facesConfig);
580                         }
581                         catch (IOException ex)
582                         {
583                             log.log(Level.SEVERE, "Error while reading faces-config from populator", ex);
584                         }
585                         catch (SAXException ex)
586                         {
587                             log.log(Level.SEVERE, "Error while reading faces-config from populator", ex);
588                         }
589                         catch (TransformerConfigurationException ex)
590                         {
591                             log.log(Level.SEVERE, "Error while reading faces-config from populator", ex);
592                         }
593                         catch (TransformerException ex)
594                         {
595                             log.log(Level.SEVERE, "Error while reading faces-config from populator", ex);
596                         }
597                     }
598                     return facesConfigList;
599                 }
600                 else
601                 {
602                     log.log(Level.SEVERE, "Cannot create xml transformer, skipping it");
603                 }
604             }
605         }
606         return Collections.emptyList();
607     }
608 
609     @Override
610     public List<FacesConfig> getFacesFlowFacesConfig(ExternalContext ectx)
611     {
612         List<FacesConfig> configFilesList;
613         Set<String> directoryPaths = ectx.getResourcePaths("/");
614         if (directoryPaths == null)
615         {
616             return Collections.emptyList();
617         }
618         configFilesList = new ArrayList<FacesConfig>();
619         
620         List<String> contextSpecifiedList = getConfigFilesList(ectx);
621         
622         for (String dirPath : directoryPaths)
623         {
624             if (dirPath.equals("/WEB-INF/"))
625             {
626                 // Look on /WEB-INF/<flow-Name>/<flowName>-flow.xml
627                 Set<String> webDirectoryPaths = ectx.getResourcePaths(dirPath);
628                 for (String webDirPath : webDirectoryPaths)
629                 {
630                     if (webDirPath.endsWith("/") && 
631                         !webDirPath.equals("/WEB-INF/classes/"))
632                     {
633                         String flowName = webDirPath.substring(9, webDirPath.length() - 1);
634                         String filePath = webDirPath+flowName+"-flow.xml";
635                         if (!contextSpecifiedList.contains(filePath))
636                         {
637                             try
638                             {
639                                 URL url = ectx.getResource(filePath);
640                                 if (url != null)
641                                 {
642                                     FacesConfig fc = parseFacesConfig(ectx, filePath, url);
643                                     if (fc != null)
644                                     {
645                                         configFilesList.add(fc);
646                                     }
647                                 }
648                             }
649                             catch (MalformedURLException ex)
650                             {
651                             }
652                         }
653                     }
654                 }
655             }
656             else if (!dirPath.startsWith("/META-INF") && dirPath.endsWith("/"))
657             {
658                 // Look on /<flowName>/<flowName>-flow.xml
659                 String flowName = dirPath.substring(1, dirPath.length() - 1);
660                 String filePath = dirPath+flowName+"-flow.xml";
661                 if (!contextSpecifiedList.contains(filePath))
662                 {
663                     try
664                     {
665                         URL url = ectx.getResource(filePath);
666                         if (url != null)
667                         {
668                             FacesConfig fc = parseFacesConfig(ectx, filePath, url);
669                             if (fc != null)
670                             {
671                                 configFilesList.add(fc);
672                             }
673                         }
674                     }
675                     catch (MalformedURLException ex)
676                     {
677                     }
678                 }
679             }
680         }
681         
682         return configFilesList;
683     }
684     
685     private FacesConfig parseFacesConfig(ExternalContext ectx, String systemId, URL url)
686     {
687         try
688         {
689             if (MyfacesConfig.getCurrentInstance(ectx).isValidateXML())
690             {
691                 //URL url = ectx.getResource(systemId);
692                 //if (url != null)
693                 //{
694                     validateFacesConfig(ectx, url);
695                 //}
696             }            
697         }
698         catch (IOException e)
699         {
700             throw new FacesException(e);
701         }
702         catch (SAXException e)
703         {
704             throw new FacesException(e);
705         }
706         InputStream stream = ectx.getResourceAsStream(systemId);
707         PushbackInputStream pbstream = new PushbackInputStream(stream, 10);
708         try
709         {
710             if (stream == null)
711             {
712                 log.severe("Faces config resource " + systemId + " not found");
713                 return null;
714             }
715             String flowName = systemId.substring(systemId.lastIndexOf('/')+1, systemId.lastIndexOf("-flow.xml"));
716             int c = pbstream.read();
717             if (c != -1)
718             {
719                 pbstream.unread(c);
720             }
721             else
722             {
723                 // Zero lenght, if that so the flow definition must be implicitly derived. 
724                 // See JSF 2.2 section 11.4.3.3
725                 // 
726                 FacesConfigImpl facesConfig = new FacesConfigImpl();
727                 FacesFlowDefinitionImpl flow = new FacesFlowDefinitionImpl();
728                 flow.setId(flowName);
729                 // In this case the defining document id is implicit associated
730                 flow.setDefiningDocumentId(systemId);
731                 
732                 String startNodePath = systemId.substring(0, systemId.lastIndexOf('/')+1)+flowName+".xhtml";
733                 //URL startNodeUrl = ectx.getResource(startNodePath);
734                 //if (startNodeUrl != null)
735                 //{
736                 flow.setStartNode(startNodePath);
737                 //}
738                 
739                 // There is a default return node with name [flow-name]-return and 
740                 // that by default points to an outer /[flow-name]-return outcome
741                 FacesFlowReturnImpl returnNode = new FacesFlowReturnImpl();
742                 returnNode.setId(flowName+"-return");
743                 NavigationCaseImpl returnNavCase = new NavigationCaseImpl();
744                 returnNavCase.setFromOutcome("/"+flowName+"-return");
745                 returnNode.setNavigationCase(returnNavCase);
746                 flow.addReturn(returnNode);
747                 
748                 facesConfig.addFacesFlowDefinition(flow);
749                 return facesConfig;
750             }
751 
752             if (log.isLoggable(Level.INFO))
753             {
754                 log.info("Reading config " + systemId);
755             }
756             
757             FacesConfigImpl facesConfig = (FacesConfigImpl) 
758                 getUnmarshaller(ectx).getFacesConfig(pbstream, systemId);
759             
760             // Set default start node when it is not present.
761             for (FacesFlowDefinition definition : facesConfig.getFacesFlowDefinitions())
762             {
763                 if (flowName.equals(definition.getId()))
764                 {
765                     FacesFlowDefinitionImpl flow = (FacesFlowDefinitionImpl) definition;
766                     if (flow.getStartNode() == null)
767                     {
768                         String startNodePath = systemId.substring(0, 
769                             systemId.lastIndexOf('/')+1)+flowName+".xhtml";
770                         flow.setStartNode(startNodePath);
771                     }
772                 }
773             }
774             return facesConfig;
775             //getDispenser().feed(getUnmarshaller().getFacesConfig(stream, systemId));
776         }
777         catch (IOException e)
778         {
779             throw new FacesException(e);
780         }
781         catch (SAXException e)
782         {
783             throw new FacesException(e);
784         }
785         finally
786         {
787             if (stream != null)
788             {
789                 try
790                 {
791                     stream.close();
792                 }
793                 catch (IOException ex)
794                 {
795                     // No op
796                 }
797             }
798         }
799     }
800 
801     @Override
802     public List<FacesConfig> getFaceletTaglibFacesConfig(ExternalContext externalContext)
803     {
804         List<FacesConfig> facesConfigFilesList = new ArrayList<FacesConfig>();
805         
806         String param = WebConfigParamUtils.getStringInitParameter(externalContext, PARAMS_LIBRARIES);
807         if (param != null)
808         {
809             for (String library : param.split(";"))
810             {
811                 try
812                 {
813                     URL src = externalContext.getResource(library.trim());
814                     if (src == null)
815                     {
816                         throw new FileNotFoundException(library);
817                     }
818                     
819                     FaceletTagLibrary tl = TagLibraryConfigUnmarshallerImpl.create(externalContext, src);
820                     if (tl != null)
821                     {
822                         org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl config = 
823                             new org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl();
824                         config.addFaceletTagLibrary(tl);
825                         facesConfigFilesList.add(config);
826                     }
827                     if (log.isLoggable(Level.FINE))
828                     {
829                         log.fine("Successfully loaded library: " + library);
830                     }
831                 }
832                 catch (IOException e)
833                 {
834                     log.log(Level.SEVERE, "Error Loading library: " + library, e);
835                 }
836             }
837         }
838         
839         try
840         {
841             FaceletConfigResourceProvider provider = FaceletConfigResourceProviderFactory.
842                 getFacesConfigResourceProviderFactory(externalContext).
843                     createFaceletConfigResourceProvider(externalContext);
844             Collection<URL> urls = provider.getFaceletTagLibConfigurationResources(externalContext);
845             for (URL url : urls)
846             {
847                 try
848                 {
849                     FaceletTagLibrary tl = TagLibraryConfigUnmarshallerImpl.create(externalContext, url);
850                     if (tl != null)
851                     {
852                         org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl config = 
853                             new org.apache.myfaces.config.impl.digester.elements.FacesConfigImpl();
854                         config.addFaceletTagLibrary(tl);
855                         facesConfigFilesList.add(config);
856                     }
857                     if (log.isLoggable(Level.FINE))
858                     {
859                         //log.fine("Added Library from: " + urls[i]);
860                         log.fine("Added Library from: " + url);
861                     }
862                 }
863                 catch (Exception e)
864                 {
865                     //log.log(Level.SEVERE, "Error Loading Library: " + urls[i], e);
866                     log.log(Level.SEVERE, "Error Loading Library: " + url, e);
867                 }
868             }
869         }
870         catch (IOException e)
871         {
872             log.log(Level.SEVERE, "Compiler Initialization Error", e);
873         }
874         return facesConfigFilesList;
875     }
876 }