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