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 org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.myfaces.application.ApplicationFactoryImpl;
24  import org.apache.myfaces.config.element.ManagedBean;
25  import org.apache.myfaces.config.element.NavigationRule;
26  import org.apache.myfaces.config.element.Renderer;
27  import org.apache.myfaces.config.impl.digester.DigesterFacesConfigDispenserImpl;
28  import org.apache.myfaces.config.impl.digester.DigesterFacesConfigUnmarshallerImpl;
29  import org.apache.myfaces.context.FacesContextFactoryImpl;
30  import org.apache.myfaces.lifecycle.LifecycleFactoryImpl;
31  import org.apache.myfaces.renderkit.RenderKitFactoryImpl;
32  import org.apache.myfaces.renderkit.html.HtmlRenderKitImpl;
33  import org.apache.myfaces.shared_impl.config.MyfacesConfig;
34  import org.apache.myfaces.shared_impl.util.ClassUtils;
35  import org.apache.myfaces.shared_impl.util.LocaleUtils;
36  import org.apache.myfaces.shared_impl.util.StateUtils;
37  import org.apache.myfaces.shared_impl.util.serial.DefaultSerialFactory;
38  import org.apache.myfaces.shared_impl.util.serial.SerialFactory;
39  import org.xml.sax.SAXException;
40  
41  import javax.faces.FacesException;
42  import javax.faces.FactoryFinder;
43  import javax.faces.application.*;
44  import javax.faces.context.ExternalContext;
45  import javax.faces.el.PropertyResolver;
46  import javax.faces.el.VariableResolver;
47  import javax.faces.event.ActionListener;
48  import javax.faces.event.PhaseListener;
49  import javax.faces.lifecycle.Lifecycle;
50  import javax.faces.lifecycle.LifecycleFactory;
51  import javax.faces.render.RenderKit;
52  import javax.faces.render.RenderKitFactory;
53  import javax.faces.webapp.FacesServlet;
54  import java.io.*;
55  import java.lang.reflect.Constructor;
56  import java.lang.reflect.InvocationTargetException;
57  import java.lang.reflect.Method;
58  import java.net.JarURLConnection;
59  import java.net.URL;
60  import java.net.URLConnection;
61  import java.util.*;
62  
63  
64  /**
65   * Configures everything for a given context.
66   * The FacesConfigurator is independent of the concrete implementations that lie
67   * behind FacesConfigUnmarshaller and FacesConfigDispenser.
68   *
69   * @author Manfred Geiler (latest modification by $Author: lofwyr $)
70   * @version $Revision: 1091751 $ $Date: 2011-04-13 06:29:11 -0500 (Wed, 13 Apr 2011) $
71   */
72  public class FacesConfigurator
73  {
74      private static final Log log = LogFactory.getLog(FacesConfigurator.class);
75  
76      private static final String STANDARD_FACES_CONFIG_RESOURCE
77          = "org.apache.myfaces.resource".replace('.', '/') + "/standard-faces-config.xml";
78      private static final String FACES_CONFIG_RESOURCE = "META-INF/faces-config.xml";
79  
80      private static final String META_INF_SERVICES_RESOURCE_PREFIX = "META-INF/services/";
81  
82      private static final String DEFAULT_RENDER_KIT_CLASS = HtmlRenderKitImpl.class.getName();
83      private static final String DEFAULT_APPLICATION_FACTORY = ApplicationFactoryImpl.class.getName();
84      private static final String DEFAULT_FACES_CONTEXT_FACTORY = FacesContextFactoryImpl.class.getName();
85      private static final String DEFAULT_LIFECYCLE_FACTORY = LifecycleFactoryImpl.class.getName();
86      private static final String DEFAULT_RENDER_KIT_FACTORY = RenderKitFactoryImpl.class.getName();
87      private static final String DEFAULT_FACES_CONFIG = "/WEB-INF/faces-config.xml";
88  
89      private static final Set FACTORY_NAMES  = new HashSet();
90      {
91          FACTORY_NAMES.add(FactoryFinder.APPLICATION_FACTORY);
92          FACTORY_NAMES.add(FactoryFinder.FACES_CONTEXT_FACTORY);
93          FACTORY_NAMES.add(FactoryFinder.LIFECYCLE_FACTORY);
94          FACTORY_NAMES.add(FactoryFinder.RENDER_KIT_FACTORY);
95      }
96  
97  
98      private ExternalContext _externalContext;
99      private FacesConfigUnmarshaller _unmarshaller;
100     private FacesConfigDispenser _dispenser;
101     private static final String JAR_EXTENSION = ".jar";
102     private static final String META_INF_MANIFEST_SUFFIX = "!/META-INF/MANIFEST.MF";
103     private static final String JAR_PREFIX = "jar:";
104 
105     private static long lastUpdate;
106 
107     public static final String MYFACES_API_PACKAGE_NAME = "myfaces-api";
108     public static final String MYFACES_IMPL_PACKAGE_NAME = "myfaces-impl";
109     public static final String MYFACES_TOMAHAWK_PACKAGE_NAME = "tomahawk";
110     public static final String MYFACES_TOMAHAWK_SANDBOX_PACKAGE_NAME = "tomahawk-sandbox";
111     public static final String MYFACES_TOBAGO_PACKAGE_NAME = "tobago-core";
112     public static final String COMMONS_EL_PACKAGE_NAME = "commons-el";
113     public static final String JSP_API_PACKAGE_NAME = "jsp-api";
114 
115     public FacesConfigurator(ExternalContext externalContext)
116     {
117         _externalContext = externalContext;
118 
119     }
120 
121     private long getResourceLastModified(String resource)
122     {
123         try 
124         {
125             URL url =  _externalContext.getResource(resource);
126             if (url != null)
127             {
128                 return getResourceLastModified(url);
129             }
130         }
131         catch (IOException e)
132         {
133             log.error("Could not read resource " + resource, e);
134         }
135         return 0;
136     }
137 
138     //Taken from trinidad URLUtils
139     private long getResourceLastModified(URL url) throws IOException
140     {
141         if ("file".equals(url.getProtocol()))
142         {
143             String externalForm = url.toExternalForm();
144             // Remove the "file:"
145             File file = new File(externalForm.substring(5));
146 
147             return file.lastModified();
148         }
149         else
150         {
151             return getResourceLastModified(url.openConnection());
152         }
153     }
154 
155     //Taken from trinidad URLUtils
156     private long getResourceLastModified(URLConnection connection) throws IOException
157     {
158         long modified;
159         if (connection instanceof JarURLConnection)
160         {
161             // The following hack is required to work-around a JDK bug.
162             // getLastModified() on a JAR entry URL delegates to the actual JAR file
163             // rather than the JAR entry.
164             // This opens internally, and does not close, an input stream to the JAR
165             // file.
166             // In turn, you cannot close it by yourself, because it's internal.
167             // The work-around is to get the modification date of the JAR file
168             // manually,
169             // and then close that connection again.
170 
171             URL jarFileUrl = ((JarURLConnection) connection).getJarFileURL();
172             URLConnection jarFileConnection = jarFileUrl.openConnection();
173 
174             try
175             {
176                 modified = jarFileConnection.getLastModified();
177             }
178             finally
179             {
180                 try
181                 {
182                     jarFileConnection.getInputStream().close();
183                 }
184                 catch (Exception exception)
185                 {
186                     // Ignored
187                 }
188             }
189         }
190         else
191         {
192             modified = connection.getLastModified();
193         }
194 
195         return modified;
196     }    
197 
198     private long getLastModifiedTime(){
199         long lastModified = 0;
200         long resModified;
201 
202         resModified = getResourceLastModified(DEFAULT_FACES_CONFIG);
203         if (resModified > lastModified)
204             lastModified = resModified;
205 
206 
207         List configFilesList = getConfigFilesList();
208 
209         for (int i = 0; i < configFilesList.size(); i++) {
210             String systemId = (String) configFilesList.get(i);
211 
212             resModified = getResourceLastModified(systemId);
213                 if (resModified > lastModified)
214                     lastModified = resModified;
215 
216         }
217 
218         return lastModified;
219     }
220 
221     public void update(){
222         long refreshPeriod = (MyfacesConfig.getCurrentInstance(_externalContext).getConfigRefreshPeriod())*1000;
223 
224         if (refreshPeriod > 0){
225             long ttl = lastUpdate + refreshPeriod;
226             if ((System.currentTimeMillis() > ttl) && (getLastModifiedTime() > ttl)) {
227                 log.info("Faces config-files are being reloaded. If you don't want this reload to happen (e.g. in production), set the web-xml-parameter: "+MyfacesConfig.INIT_PARAM_CONFIG_REFRESH_PERIOD +" to -1.");
228                 purgeConfiguration();
229                 configure();
230             }
231         }
232     }
233 
234     private void purgeConfiguration() {
235         Method appplicationPurgeMethod = null;
236         Method renderKitPurgeMethod = null;
237         Method lifecyclePurgeMethod = null;
238         
239         Class[] emptyParameterList = new Class[]{};
240         
241         ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
242         RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
243         LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
244         
245         try {
246 
247             appplicationPurgeMethod = applicationFactory.getClass().getMethod("purgeApplication", emptyParameterList);
248             renderKitPurgeMethod = renderKitFactory.getClass().getMethod("purgeRenderKit", emptyParameterList);
249             lifecyclePurgeMethod = lifecycleFactory.getClass().getMethod("purgeLifecycle", emptyParameterList);
250             // factories and serial factory need not be purged...
251         }
252         catch (NoSuchMethodException e)
253         {
254             log.error("Configuration objects do not support clean-up. Update aborted",e);
255             return;
256         } 
257         
258         if (appplicationPurgeMethod != null && renderKitPurgeMethod != null && lifecyclePurgeMethod != null)
259         {
260             try
261             {
262                 RuntimeConfig.getCurrentInstance(_externalContext).purge();
263                 appplicationPurgeMethod.invoke(applicationFactory, emptyParameterList);
264                 renderKitPurgeMethod.invoke(renderKitFactory, emptyParameterList);
265                 lifecyclePurgeMethod.invoke(lifecycleFactory, emptyParameterList);
266             }
267             catch (IllegalAccessException e)
268             {
269                 log.fatal("Error during configuration clean-up" + e.getMessage(),e);
270             }
271             catch (InvocationTargetException e)
272             {
273                 log.fatal("Error during configuration clean-up" + e.getMessage(),e);
274             }
275         }
276     }
277 
278     public void configure()
279         throws FacesException
280     {
281         //These two classes can be easily replaced by alternative implementations.
282         //As long as there is no need to switch implementations we need no
283         //factory pattern to create them.
284         _unmarshaller = new DigesterFacesConfigUnmarshallerImpl(_externalContext);
285         _dispenser = new DigesterFacesConfigDispenserImpl();
286 
287         try
288         {
289             feedStandardConfig();
290             feedMetaInfServicesFactories();
291             //feedJarFileConfigurations();
292             feedClassloaderConfigurations();
293             feedContextSpecifiedConfig();
294             feedWebAppConfig();
295 
296             if(log.isInfoEnabled())
297             {
298                 logMetaInf();
299             }
300         } catch (IOException e)
301         {
302             throw new FacesException(e);
303         } catch (SAXException e)
304         {
305             throw new FacesException(e);
306         }
307 
308         configureFactories();
309         configureApplication();
310         configureRenderKits();
311         configureRuntimeConfig();
312         configureLifecycle();
313         handleSerialFactory();
314 
315         //record the time of update
316         lastUpdate = System.currentTimeMillis();
317     }
318 
319     private void feedStandardConfig() throws IOException, SAXException
320     {
321         InputStream stream = ClassUtils.getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE);
322         if (stream == null) throw new FacesException("Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found");
323         if (log.isInfoEnabled()) log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE);
324         _dispenser.feed(_unmarshaller.getFacesConfig(stream, STANDARD_FACES_CONFIG_RESOURCE));
325         stream.close();
326     }
327 
328     /**
329      * This method performs part of the factory search outlined in section 10.2.6.1.
330      */
331     protected void logMetaInf()
332     {
333         try
334         {
335             List li = new ArrayList();
336             li.add(new VersionInfo(MYFACES_API_PACKAGE_NAME));
337             li.add(new VersionInfo(MYFACES_IMPL_PACKAGE_NAME));
338             li.add(new VersionInfo(MYFACES_TOMAHAWK_SANDBOX_PACKAGE_NAME));
339             li.add(new VersionInfo(MYFACES_TOMAHAWK_PACKAGE_NAME));
340             li.add(new VersionInfo(MYFACES_TOBAGO_PACKAGE_NAME));
341 
342             Iterator it = ClassUtils.getResources("META-INF/MANIFEST.MF",
343                                                   this);
344             while (it.hasNext())
345             {
346                 URL url = (URL)it.next();
347 
348                 for (int i = 0; i < li.size(); i++)
349                 {
350                     VersionInfo versionInfo = (VersionInfo) li.get(i);
351                     if(checkJar(versionInfo, url))
352                         break;
353                 }
354             }
355 
356             for (int i = 0; i < li.size(); i++)
357             {
358                 VersionInfo versionInfo = (VersionInfo) li.get(i);
359 
360                 if(versionInfo.getUsedVersion()!=null)
361                 {
362                     if(log.isInfoEnabled())
363                     {
364                         log.info("Artifact '" + versionInfo.getPackageName()
365                             + "' was found in version '" + versionInfo.getUsedVersion()
366                             + "' from path '" + versionInfo.getUsedVersionPath() + "'");
367                     }
368                 }
369                 else
370                 {
371                     if(log.isInfoEnabled())
372                     {
373                         log.info("Artifact '" + versionInfo.getPackageName() + "' was not found.");
374                     }
375                 }
376             }
377         }
378         catch (Throwable e)
379         {
380             throw new FacesException(e);
381         }
382     }
383 
384     private static boolean checkJar(VersionInfo versionInfo, URL path)
385     {
386         int index;
387 
388         String version = versionInfo.getLastVersion();
389 
390         String pathString = path.toString();
391 
392         if(!pathString.startsWith(JAR_PREFIX))
393             return false;
394 
395         if(!(pathString.length()>(META_INF_MANIFEST_SUFFIX.length()+JAR_PREFIX.length())))
396         {
397             if(log.isDebugEnabled())
398                 log.debug("PathString : "+pathString+" not long enough to be parsed.");
399             return false;
400         }
401 
402         pathString = pathString.substring(JAR_PREFIX.length(),pathString.length()-META_INF_MANIFEST_SUFFIX.length());
403 
404         File file = new File(pathString);
405 
406         String fileName = file.getName();
407 
408         if(fileName.endsWith(JAR_EXTENSION) && ((index=fileName.indexOf(versionInfo.getPackageName()))!=-1))
409         {
410             int beginIndex = index+versionInfo.getPackageName().length()+1;
411 
412             if(beginIndex > fileName.length()-1)
413             {
414                 log.debug("beginIndex out of bounds. fileName: "+fileName);
415                 return false;
416             }
417 
418             int endIndex = fileName.length()-JAR_EXTENSION.length();
419 
420             if(endIndex<0 || endIndex<=beginIndex)
421             {
422                 log.debug("endIndex out of bounds. fileName: "+fileName);
423                 return false;
424             }
425 
426             String newVersion = fileName.substring(beginIndex, endIndex);
427 
428             if(version == null)
429             {
430                 versionInfo.addJarInfo(pathString,newVersion);
431            }
432             else if(version.equals(newVersion))
433             {
434                 versionInfo.addJarInfo(pathString, version);
435             }
436             else
437             {
438                 log.error("You are using the MyFaces-package : "+versionInfo.getPackageName() +
439                         " in different versions; first (and probably used) version is : "+versionInfo.getUsedVersion() +", currently encountered version is : "+newVersion+
440                         ". This will cause undesired behaviour. Please clean out your class-path." +
441                         " The first encountered version is loaded from : "+versionInfo.getUsedVersionPath()+". The currently encountered version is loaded from : "+
442                         path);
443             }
444 
445             return true;
446         }
447 
448         return false;
449     }
450 
451 
452     /**
453      * This method performs part of the factory search outlined in section 10.2.6.1.
454      */
455     protected void feedMetaInfServicesFactories()
456     {
457         try
458         {
459             for (Iterator iterator = FACTORY_NAMES.iterator(); iterator.hasNext();)
460             {
461                 String factoryName = (String)iterator.next();
462                 Iterator it = ClassUtils.getResources(META_INF_SERVICES_RESOURCE_PREFIX + factoryName,
463                                                       this);
464                 while (it.hasNext())
465                 {
466                     URL url = (URL)it.next();
467                     InputStream stream = openStreamWithoutCache(url);
468                     InputStreamReader isr = new InputStreamReader(stream);
469                     BufferedReader br = new BufferedReader(isr);
470                     String className;
471                     try
472                     {
473                         className = br.readLine();
474                     }
475                     catch (IOException e)
476                     {
477                         throw new FacesException("Unable to read class name from file "
478                                                  + url.toExternalForm(), e);
479                     }
480                     br.close();
481                     isr.close();
482                     stream.close();
483 
484                     if (log.isInfoEnabled()) log.info("Found " + factoryName + " factory implementation: " + className);
485 
486                     if (factoryName.equals(FactoryFinder.APPLICATION_FACTORY))
487                     {
488                         _dispenser.feedApplicationFactory(className);
489                     } else if (factoryName.equals(FactoryFinder.FACES_CONTEXT_FACTORY))
490                     {
491                         _dispenser.feedFacesContextFactory(className);
492                     } else if (factoryName.equals(FactoryFinder.LIFECYCLE_FACTORY))
493                     {
494                         _dispenser.feedLifecycleFactory(className);
495                     } else if (factoryName.equals(FactoryFinder.RENDER_KIT_FACTORY))
496                     {
497                         _dispenser.feedRenderKitFactory(className);
498                     } else
499                     {
500                         throw new IllegalStateException("Unexpected factory name " + factoryName);
501                     }
502                 }
503             }
504         }
505         catch (Throwable e)
506         {
507             throw new FacesException(e);
508         }
509     }
510 
511     private InputStream openStreamWithoutCache(URL url)
512             throws IOException
513     {
514         URLConnection connection = url.openConnection();
515         connection.setUseCaches(false);
516         return connection.getInputStream();
517     }
518 
519     /*private Map expandFactoryNames(Set factoryNames)
520    {
521        Map names = new HashMap();
522        Iterator itr = factoryNames.iterator();
523        while (itr.hasNext())
524        {
525            String name = (String) itr.next();
526            names.put(META_INF_SERVICES_LOCATION + name, name);
527        }
528        return names;
529    } */
530 
531 
532     /**
533      * This method fixes MYFACES-208
534      */
535     private void feedClassloaderConfigurations()
536     {
537         try
538         {
539             Iterator it = ClassUtils.getResources(FACES_CONFIG_RESOURCE, this);
540             while (it.hasNext())
541             {
542                 URL url = (URL)it.next();
543                 InputStream stream = openStreamWithoutCache(url);
544                 String systemId = url.toExternalForm();
545                 if (log.isInfoEnabled()) log.info("Reading config " + systemId);
546                 _dispenser.feed(_unmarshaller.getFacesConfig(stream, systemId));
547                 stream.close();
548             }
549         }
550         catch (Throwable e)
551         {
552             throw new FacesException(e);
553         }
554     }
555 
556 
557     /*
558      * To make this easier AND to fix MYFACES-208 at the same time, this method is replaced by
559      * {@link FacesConfigurator#feedClassloaderConfigurations}
560      *
561      * @deprecated {@link FacesConfigurator#feedClassloaderConfigurations} replaces this one
562 
563     private void feedJarFileConfigurations()
564     {
565         Set jars = _externalContext.getResourcePaths("/WEB-INF/lib/");
566         if (jars != null)
567         {
568             for (Iterator it = jars.iterator(); it.hasNext();)
569             {
570                 String path = (String) it.next();
571                 if (path.toLowerCase().endsWith(".jar"))
572                 {
573                     feedJarConfig(path);
574                 }
575             }
576         }
577     }
578 
579     private void feedJarConfig(String jarPath)
580         throws FacesException
581     {
582         try
583         {
584             // not all containers expand archives, so we have to do it the generic way:
585             // 1. get the stream from external context
586             InputStream in = _externalContext.getResourceAsStream(jarPath);
587             if (in == null)
588             {
589                 if (jarPath.startsWith("/"))
590                 {
591                     in = _externalContext.getResourceAsStream(jarPath.substring(1));
592                 } else
593                 {
594                     in = _externalContext.getResourceAsStream("/" + jarPath);
595                 }
596             }
597             if (in == null)
598             {
599                 log.error("Resource " + jarPath + " not found");
600                 return;
601             }
602 
603             // 2. search the jar stream for META-INF/faces-config.xml
604             JarInputStream jar = new JarInputStream(in);
605             JarEntry entry = jar.getNextJarEntry();
606             boolean found = false;
607 
608             while (entry != null)
609             {
610                 if (entry.getName().equals(FACES_CONFIG_RESOURCE))
611                 {
612                     if (log.isDebugEnabled()) log.debug("faces-config.xml found in " + jarPath);
613                     found = true;
614                     break;
615                 }
616                 entry = jar.getNextJarEntry();
617             }
618             jar.close();
619 
620             File tmp;
621 
622             // 3. if faces-config.xml was found, extract the jar and copy it to a temp file; hand over the temp file
623             // to the parser and delete it afterwards
624             if (found)
625             {
626                 tmp = File.createTempFile("myfaces", ".jar");
627                 tmp.deleteOnExit(); // just to make sure the file will be deleted
628 
629                 in = _externalContext.getResourceAsStream(jarPath);
630                 FileOutputStream out = new FileOutputStream(tmp);
631                 byte[] buffer = new byte[4096];
632                 int r;
633 
634                 while ((r = in.read(buffer)) != -1)
635                 {
636                     out.write(buffer, 0, r);
637                 }
638                 out.close();
639 
640                 JarFile jarFile = new JarFile(tmp);
641                 try
642                 {
643                     JarEntry configFile = jarFile.getJarEntry(FACES_CONFIG_RESOURCE);
644                     if (configFile != null)
645                     {
646                         if (log.isInfoEnabled()) log.info("faces-config.xml found in jar " + jarPath);
647                         InputStream stream = jarFile.getInputStream(configFile);
648                         String systemId = "jar:" + tmp.toURL() + "!/" + configFile.getName();
649                         if (log.isDebugEnabled()) log.debug("Reading config " + systemId);
650                         _dispenser.feed(_unmarshaller.getFacesConfig(stream, systemId));
651                         stream.close();
652                     }
653                 } finally
654                 {
655                     jarFile.close();
656                     tmp.delete();
657                 }
658             } else
659             {
660                 if (log.isDebugEnabled()) log.debug("Jar " + jarPath + " contains no faces-config.xml");
661             }
662         } catch (Exception e)
663         {
664             throw new FacesException(e);
665         }
666     }
667      */
668 
669     private void feedContextSpecifiedConfig() throws IOException, SAXException
670     {
671         List configFilesList = getConfigFilesList();
672         for (int i = 0; i < configFilesList.size(); i++) {
673             String systemId = (String) configFilesList.get(i);
674             InputStream stream = _externalContext.getResourceAsStream(systemId);
675             if (stream == null)
676             {
677                 log.error("Faces config resource " + systemId + " not found");
678                 continue;
679             }
680 
681             if (log.isInfoEnabled()) log.info("Reading config " + systemId);
682             _dispenser.feed(_unmarshaller.getFacesConfig(stream, systemId));
683             stream.close();
684         }
685     }
686 
687     private List getConfigFilesList() {
688         String configFiles = _externalContext.getInitParameter(FacesServlet.CONFIG_FILES_ATTR);
689         List configFilesList = new ArrayList();
690         if (configFiles != null)
691         {
692             StringTokenizer st = new StringTokenizer(configFiles, ",", false);
693             while (st.hasMoreTokens())
694             {
695                 String systemId = st.nextToken().trim();
696 
697                 if (DEFAULT_FACES_CONFIG.equals(systemId))
698                 {
699                     if(log.isWarnEnabled())
700                         log.warn(DEFAULT_FACES_CONFIG + " has been specified in the " +
701                                 FacesServlet.CONFIG_FILES_ATTR + " context parameter of " +
702                                 "the deployment descriptor. This will automatically be removed, " +
703                                 "if we wouldn't do this, it would be loaded twice.  See JSF spec 1.1, 10.3.2");
704                 }
705                 else
706                     configFilesList.add(systemId);
707 
708 
709             }
710         }
711         return configFilesList;
712     }
713 
714 
715     private void feedWebAppConfig() throws IOException, SAXException
716     {
717         //web application config
718         InputStream stream = _externalContext.getResourceAsStream(DEFAULT_FACES_CONFIG);
719         if (stream != null)
720         {
721             if (log.isInfoEnabled()) log.info("Reading config /WEB-INF/faces-config.xml");
722             _dispenser.feed(_unmarshaller.getFacesConfig(stream, DEFAULT_FACES_CONFIG));
723             stream.close();
724         }
725     }
726 
727 
728     private void configureFactories()
729     {
730         setFactories(FactoryFinder.APPLICATION_FACTORY, _dispenser.getApplicationFactoryIterator(), DEFAULT_APPLICATION_FACTORY);
731         setFactories(FactoryFinder.FACES_CONTEXT_FACTORY, _dispenser.getFacesContextFactoryIterator(), DEFAULT_FACES_CONTEXT_FACTORY);
732         setFactories(FactoryFinder.LIFECYCLE_FACTORY, _dispenser.getLifecycleFactoryIterator(), DEFAULT_LIFECYCLE_FACTORY);
733         setFactories(FactoryFinder.RENDER_KIT_FACTORY, _dispenser.getRenderKitFactoryIterator(), DEFAULT_RENDER_KIT_FACTORY);
734     }
735 
736 
737     private void setFactories(String factoryName, Iterator factories, String defaultFactory)
738     {
739         FactoryFinder.setFactory(factoryName, defaultFactory);
740         while (factories.hasNext())
741         {
742             FactoryFinder.setFactory(factoryName, (String) factories.next());
743         }
744     }
745 
746 
747     private void configureApplication()
748     {
749         Application application = ((ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY)).getApplication();
750         application.setActionListener((ActionListener) getApplicationObject(ActionListener.class, _dispenser.getActionListenerIterator(), null));
751 
752         if (_dispenser.getDefaultLocale() != null)
753         {
754             application.setDefaultLocale(
755                 LocaleUtils.toLocale(_dispenser.getDefaultLocale()));
756         }
757 
758         if (_dispenser.getDefaultRenderKitId() != null)
759         {
760             application.setDefaultRenderKitId(_dispenser.getDefaultRenderKitId());
761         }
762 
763         if (_dispenser.getMessageBundle() != null)
764         {
765             application.setMessageBundle(_dispenser.getMessageBundle());
766         }
767 
768         application.setNavigationHandler((NavigationHandler) getApplicationObject(NavigationHandler.class,
769                                                                                   _dispenser.getNavigationHandlerIterator(), application.getNavigationHandler()));
770         application.setPropertyResolver((PropertyResolver) getApplicationObject(PropertyResolver.class,
771                                                                                 _dispenser.getPropertyResolverIterator(), application.getPropertyResolver()));
772         application.setStateManager((StateManager) getApplicationObject(StateManager.class,
773                                                                         _dispenser.getStateManagerIterator(), application.getStateManager()));
774         List locales = new ArrayList();
775         for (Iterator it = _dispenser.getSupportedLocalesIterator(); it.hasNext();)
776         {
777             locales.add(LocaleUtils.toLocale((String) it.next()));
778         }
779         application.setSupportedLocales(locales);
780 
781         application.setVariableResolver((VariableResolver) getApplicationObject(VariableResolver.class,
782                                                                                 _dispenser.getVariableResolverIterator(), application.getVariableResolver()));
783         application.setViewHandler((ViewHandler) getApplicationObject(ViewHandler.class,
784                                                                       _dispenser.getViewHandlerIterator(), application.getViewHandler()));
785 
786         for (Iterator it = _dispenser.getComponentTypes(); it.hasNext();)
787         {
788             String componentType = (String) it.next();
789             application.addComponent(componentType,
790                                      _dispenser.getComponentClass(componentType));
791         }
792 
793         for (Iterator it = _dispenser.getConverterIds(); it.hasNext();)
794         {
795             String converterId = (String) it.next();
796             application.addConverter(converterId,
797                                      _dispenser.getConverterClassById(converterId));
798         }
799 
800         for (Iterator it = _dispenser.getConverterClasses(); it.hasNext();)
801         {
802             String converterClass = (String) it.next();
803             try
804             {
805                 application.addConverter(ClassUtils.simpleClassForName(converterClass),
806                                          _dispenser.getConverterClassByClass(converterClass));
807             }
808             catch(Exception ex)
809             {
810                 log.error("Converter could not be added. Reason:",ex);
811             }
812         }
813 
814         for (Iterator it = _dispenser.getValidatorIds(); it.hasNext();)
815         {
816             String validatorId = (String) it.next();
817             application.addValidator(validatorId,
818                                      _dispenser.getValidatorClass(validatorId));
819         }
820     }
821 
822 
823     private Object getApplicationObject(Class interfaceClass, Iterator classNamesIterator, Object defaultObject)
824     {
825         Object current = defaultObject;
826 
827         while (classNamesIterator.hasNext())
828         {
829             String implClassName = (String) classNamesIterator.next();
830             Class implClass = ClassUtils.simpleClassForName(implClassName);
831 
832             // check, if class is of expected interface type
833             if (!interfaceClass.isAssignableFrom(implClass))
834             {
835                 throw new IllegalArgumentException("Class " + implClassName + " is no " + interfaceClass.getName());
836             }
837 
838             if (current == null)
839             {
840                 // nothing to decorate
841                 current = ClassUtils.newInstance(implClass);
842             } else
843             {
844                 // let's check if class supports the decorator pattern
845                 try
846                 {
847                     Constructor delegationConstructor = implClass.getConstructor(new Class[]{interfaceClass});
848                     // impl class supports decorator pattern,
849                     try
850                     {
851                         // create new decorator wrapping current
852                         current = delegationConstructor.newInstance(new Object[]{current});
853                     } catch (InstantiationException e)
854                     {
855                         log.error(e.getMessage(), e);
856                         throw new FacesException(e);
857                     } catch (IllegalAccessException e)
858                     {
859                         log.error(e.getMessage(), e);
860                         throw new FacesException(e);
861                     } catch (InvocationTargetException e)
862                     {
863                         log.error(e.getMessage(), e);
864                         throw new FacesException(e);
865                     }
866                 } catch (NoSuchMethodException e)
867                 {
868                     // no decorator pattern support
869                     current = ClassUtils.newInstance(implClass);
870                 }
871             }
872         }
873 
874         return current;
875     }
876 
877 
878     private void configureRuntimeConfig()
879     {
880         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(_externalContext);
881 
882         for (Iterator iterator = _dispenser.getManagedBeans(); iterator.hasNext();)
883         {
884             ManagedBean bean = (ManagedBean) iterator.next();
885             ManagedBean oldBean = runtimeConfig.getManagedBean(bean.getManagedBeanName());
886 
887             if(log.isWarnEnabled() && oldBean != null)
888                 log.warn("More than one managed bean w/ the name of '" 
889                         + bean.getManagedBeanName() + "' registered. First managed bean was registered in :" +
890                             oldBean.getConfigLocation()+", new managed bean was registered in : "+
891                             bean.getConfigLocation()+". The first definition of the managed-bean will be ignored by the standard MyFaces variable resolver!");
892 
893             runtimeConfig.addManagedBean(bean.getManagedBeanName(), bean);
894 
895         }
896 
897         removePurgedBeansFromSessionAndApplication(runtimeConfig);
898 
899         for (Iterator iterator = _dispenser.getNavigationRules(); iterator.hasNext();)
900         {
901             NavigationRule rule = (NavigationRule) iterator.next();
902             runtimeConfig.addNavigationRule(rule);
903 
904         }
905         
906         for (Iterator it = _dispenser.getConverterConfigurationByClassName(); it.hasNext();)
907         {
908             String converterClassName = (String) it.next();
909 
910             runtimeConfig.addConverterConfiguration(converterClassName,
911                                                                       _dispenser.getConverterConfiguration(converterClassName));
912         }
913     }
914 
915     private void removePurgedBeansFromSessionAndApplication(RuntimeConfig runtimeConfig) {
916         Map oldManagedBeans = runtimeConfig.getManagedBeansNotReaddedAfterPurge();
917         if(oldManagedBeans!=null) {
918             Iterator it=oldManagedBeans.entrySet().iterator();
919             while(it.hasNext()) {
920                 Map.Entry entry = (Map.Entry) it.next();
921                 ManagedBean bean = (ManagedBean) entry.getValue();
922 
923                 String scope = bean.getManagedBeanScope();
924 
925                 if(scope!=null && scope.equalsIgnoreCase("session")) {
926                     _externalContext.getSessionMap().remove(entry.getKey());
927                 }
928                 else if(scope!=null && scope.equalsIgnoreCase("application")) {
929                     _externalContext.getApplicationMap().remove(entry.getKey());
930                 }
931             }
932         }
933         runtimeConfig.resetManagedBeansNotReaddedAfterPurge();
934     }
935 
936 
937     private void configureRenderKits()
938     {
939         RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
940 
941         for (Iterator iterator = _dispenser.getRenderKitIds(); iterator.hasNext();)
942         {
943             String renderKitId = (String) iterator.next();
944             String renderKitClass = _dispenser.getRenderKitClass(renderKitId);
945 
946             if (renderKitClass == null)
947             {
948                 renderKitClass = DEFAULT_RENDER_KIT_CLASS;
949             }
950 
951             RenderKit renderKit = (RenderKit) ClassUtils.newInstance(renderKitClass);
952 
953             for (Iterator renderers = _dispenser.getRenderers(renderKitId); renderers.hasNext();)
954             {
955                 Renderer element = (Renderer) renderers.next();
956                 javax.faces.render.Renderer renderer;
957                 try {
958                   renderer = (javax.faces.render.Renderer) ClassUtils.newInstance(element.getRendererClass());
959                 } catch(Throwable e) {
960                   // ignore the failure so that the render kit is configured
961                   log.error("failed to configure class " + element.getRendererClass(), e);
962                   continue;
963                 }
964 
965                 renderKit.addRenderer(element.getComponentFamily(), element.getRendererType(), renderer);
966             }
967 
968             renderKitFactory.addRenderKit(renderKitId, renderKit);
969         }
970     }
971 
972 
973     private void configureLifecycle()
974     {
975         // create the lifecycle used by the app
976         LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
977         Lifecycle lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
978 
979         // add phase listeners
980         for (Iterator iterator = _dispenser.getLifecyclePhaseListeners(); iterator.hasNext();)
981         {
982             String listenerClassName = (String) iterator.next();
983             try
984             {
985                 lifecycle.addPhaseListener((PhaseListener) ClassUtils.newInstance(listenerClassName));
986             } catch (ClassCastException e)
987             {
988                 log.error("Class " + listenerClassName + " does not implement PhaseListener",e);
989             }
990         }
991     }
992 
993 
994     private String getLifecycleId()
995     {
996         String id = _externalContext.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
997 
998         if (id != null)
999         {
1000             return id;
1001         }
1002 
1003         return LifecycleFactory.DEFAULT_LIFECYCLE;
1004     }
1005 
1006     public static class VersionInfo
1007     {
1008         private String packageName;
1009         private List jarInfos;
1010 
1011         public VersionInfo(String packageName)
1012         {
1013 
1014             this.packageName = packageName;
1015         }
1016 
1017         public String getPackageName()
1018         {
1019             return packageName;
1020         }
1021 
1022         public void setPackageName(String packageName)
1023         {
1024             this.packageName = packageName;
1025         }
1026 
1027         public void addJarInfo(String path, String version)
1028         {
1029             if(jarInfos == null)
1030             {
1031                 jarInfos = new ArrayList();
1032             }
1033 
1034             jarInfos.add(new JarInfo(path, version));
1035         }
1036 
1037         public String getLastVersion()
1038         {
1039             if(jarInfos == null)
1040                 return null;
1041             if(jarInfos.size()==0)
1042                 return null;
1043 
1044             return ((JarInfo) jarInfos.get(jarInfos.size()-1)).getVersion();
1045         }
1046 
1047         /**Probably, the first encountered version will be used.
1048          *
1049          * @return probably used version
1050          */
1051         public String getUsedVersion()
1052         {
1053 
1054             if(jarInfos == null)
1055                 return null;
1056             if(jarInfos.size()==0)
1057                 return null;
1058 
1059             return ((JarInfo) jarInfos.get(0)).getVersion();
1060         }
1061 
1062         /**Probably, the first encountered version will be used.
1063          *
1064          * @return probably used classpath
1065          */
1066         public String getUsedVersionPath()
1067         {
1068 
1069             if(jarInfos == null)
1070                 return null;
1071             if(jarInfos.size()==0)
1072                 return null;
1073 
1074             return ((JarInfo) jarInfos.get(0)).getUrl();
1075 
1076         }
1077     }
1078 
1079     public static class JarInfo
1080     {
1081         private String url;
1082         private String version;
1083 
1084         public JarInfo(String url, String version)
1085         {
1086             this.url = url;
1087             this.version = version;
1088         }
1089 
1090         public String getVersion()
1091         {
1092             return version;
1093         }
1094 
1095         public void setVersion(String version)
1096         {
1097             this.version = version;
1098         }
1099 
1100         public String getUrl()
1101         {
1102             return url;
1103         }
1104 
1105         public void setUrl(String url)
1106         {
1107             this.url = url;
1108         }
1109     }
1110 
1111 
1112     private void handleSerialFactory(){
1113 
1114         String serialProvider = _externalContext.getInitParameter(StateUtils.SERIAL_FACTORY);
1115         SerialFactory serialFactory = null;
1116 
1117         if(serialProvider == null)
1118         {
1119             serialFactory = new DefaultSerialFactory();
1120         }
1121         else
1122         {
1123             try
1124             {
1125                 serialFactory = (SerialFactory) ClassUtils.newInstance(serialProvider);
1126 
1127             }catch(ClassCastException e){
1128                 log.error("Make sure '" + serialProvider +
1129                         "' implements the correct interface", e);
1130             }
1131             catch(Exception e){
1132                 log.error(e);
1133             }
1134             finally
1135             {
1136                 if(serialFactory == null)
1137                 {
1138                     serialFactory = new DefaultSerialFactory();
1139                     log.error("Using default serialization provider");
1140                 }
1141             }
1142 
1143         }
1144 
1145         log.info("Serialization provider : " + serialFactory.getClass());
1146         _externalContext.getApplicationMap().put(StateUtils.SERIAL_FACTORY, serialFactory);
1147 
1148     }
1149 
1150 }