1 /*
2 * $Id: ConfigureListener.java,v 1.117.4.2 2008/04/29 19:33:24 rlubke Exp $
3 */
4 /*
5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
6 *
7 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
8 *
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common Development
11 * and Distribution License("CDDL") (collectively, the "License"). You
12 * may not use this file except in compliance with the License. You can obtain
13 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
14 * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
15 * language governing permissions and limitations under the License.
16 *
17 * When distributing the software, include this License Header Notice in each
18 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
19 * Sun designates this particular file as subject to the "Classpath" exception
20 * as provided by Sun in the GPL Version 2 section of the License file that
21 * accompanied this code. If applicable, add the following below the License
22 * Header, with the fields enclosed by brackets [] replaced by your own
23 * identifying information: "Portions Copyrighted [year]
24 * [name of copyright owner]"
25 *
26 * Contributor(s):
27 *
28 * If you wish your version of this file to be governed by only the CDDL or
29 * only the GPL Version 2, indicate your decision by adding "[Contributor]
30 * elects to include this software in this distribution under the [CDDL or GPL
31 * Version 2] license." If you don't indicate a single choice of license, a
32 * recipient has the option to distribute your version of this file under
33 * either the CDDL, the GPL Version 2 or to extend the choice of license to
34 * its licensees as provided above. However, if you add GPL Version 2 code
35 * and therefore, elected the GPL Version 2 license, then the option applies
36 * only if the new code is made subject to such option by the copyright
37 * holder.
38 */
39
40 package com.sun.faces.config;
41
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.StringReader;
45 import java.net.URL;
46 import java.net.URLConnection;
47 import java.text.MessageFormat;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.concurrent.ScheduledThreadPoolExecutor;
52 import java.util.concurrent.TimeUnit;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55
56 import javax.el.CompositeELResolver;
57 import javax.el.ExpressionFactory;
58 import javax.faces.FactoryFinder;
59 import javax.faces.context.FacesContext;
60 import javax.servlet.ServletContext;
61 import javax.servlet.ServletContextAttributeEvent;
62 import javax.servlet.ServletContextAttributeListener;
63 import javax.servlet.ServletContextEvent;
64 import javax.servlet.ServletContextListener;
65 import javax.servlet.ServletRequestAttributeEvent;
66 import javax.servlet.ServletRequestAttributeListener;
67 import javax.servlet.ServletRequestEvent;
68 import javax.servlet.ServletRequestListener;
69 import javax.servlet.http.HttpSession;
70 import javax.servlet.http.HttpSessionAttributeListener;
71 import javax.servlet.http.HttpSessionBindingEvent;
72 import javax.servlet.http.HttpSessionEvent;
73 import javax.servlet.http.HttpSessionListener;
74 import javax.servlet.jsp.JspApplicationContext;
75 import javax.servlet.jsp.JspFactory;
76 import javax.xml.parsers.SAXParser;
77 import javax.xml.parsers.SAXParserFactory;
78
79 import com.sun.faces.application.ApplicationAssociate;
80 import com.sun.faces.application.WebappLifecycleListener;
81 import com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter;
82 import com.sun.faces.config.WebConfiguration.WebContextInitParameter;
83 import com.sun.faces.el.ELContextListenerImpl;
84 import com.sun.faces.el.ELUtils;
85 import com.sun.faces.el.FacesCompositeELResolver;
86 import com.sun.faces.scripting.GroovyHelper;
87 import com.sun.faces.scripting.GroovyHelperFactory;
88 import com.sun.faces.mgbean.BeanBuilder;
89 import com.sun.faces.mgbean.BeanManager;
90 import com.sun.faces.renderkit.RenderKitUtils;
91 import com.sun.faces.util.FacesLogger;
92 import com.sun.faces.util.MessageUtils;
93 import com.sun.faces.util.ReflectionUtils;
94 import com.sun.faces.util.Timer;
95 import com.sun.faces.util.Util;
96 import org.xml.sax.Attributes;
97 import org.xml.sax.InputSource;
98 import org.xml.sax.SAXException;
99 import org.xml.sax.helpers.DefaultHandler;
100
101 /**
102 * <p>Parse all relevant JavaServer Faces configuration resources, and
103 * configure the Reference Implementation runtime environment.</p>
104 * <p/>
105 */
106 public class ConfigureListener implements ServletRequestListener,
107 HttpSessionListener,
108 ServletRequestAttributeListener,
109 HttpSessionAttributeListener,
110 ServletContextAttributeListener,
111 ServletContextListener {
112
113
114 private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();
115
116 private ScheduledThreadPoolExecutor webResourcePool =
117 new ScheduledThreadPoolExecutor(1);
118
119 protected WebappLifecycleListener webAppListener;
120 protected WebConfiguration webConfig;
121
122
123
124 // ------------------------------------------ ServletContextListener Methods
125
126
127 public void contextInitialized(ServletContextEvent sce) {
128 ServletContext context = sce.getServletContext();
129 webAppListener = new WebappLifecycleListener(context);
130 webAppListener.contextInitialized(sce);
131 Timer timer = Timer.getInstance();
132 if (timer != null) {
133 timer.startTiming();
134 }
135
136
137 if (LOGGER.isLoggable(Level.FINE)) {
138 LOGGER.log(Level.FINE,
139 MessageFormat.format(
140 "ConfigureListener.contextInitialized({0})",
141 getServletContextIdentifier(context)));
142 }
143
144 webConfig = WebConfiguration.getInstance(context);
145 ConfigManager configManager = ConfigManager.getInstance();
146
147 if (configManager.hasBeenInitialized(context)) {
148 return;
149 }
150
151 // Check to see if the FacesServlet is present in the
152 // web.xml. If it is, perform faces configuration as normal,
153 // otherwise, simply return.
154 if (!webConfig.isOptionEnabled(BooleanWebContextInitParameter.ForceLoadFacesConfigFiles)) {
155 WebXmlProcessor processor = new WebXmlProcessor(context);
156 if (!processor.isFacesServletPresent()) {
157 if (LOGGER.isLoggable(Level.FINE)) {
158 LOGGER.log(Level.FINE,
159 "No FacesServlet found in deployment descriptor - bypassing configuration");
160 }
161 WebConfiguration.clear(context);
162 return;
163 } else {
164 if (LOGGER.isLoggable(Level.FINE)) {
165 LOGGER.log(Level.FINE,
166 "FacesServlet found in deployment descriptor - processing configuration.");
167 }
168 }
169 }
170
171 FacesContext initContext = new InitFacesContext(context);
172 ReflectionUtils.initCache(Thread.currentThread().getContextClassLoader());
173
174 try {
175
176 if (LOGGER.isLoggable(Level.INFO)) {
177 LOGGER.log(Level.INFO,
178 "jsf.config.listener.version",
179 getServletContextIdentifier(context));
180 }
181
182 // see if we need to disable our TLValidator
183 Util.setHtmlTLVActive(
184 webConfig.isOptionEnabled(BooleanWebContextInitParameter.EnableHtmlTagLibraryValidator));
185
186 if (webConfig.isOptionEnabled(BooleanWebContextInitParameter.VerifyFacesConfigObjects)) {
187 if (LOGGER.isLoggable(Level.WARNING)) {
188 LOGGER.warning("jsf.config.verifyobjects.development_only");
189 }
190 // if we're verifying, force bean validation to occur at startup as well
191 webConfig.overrideContextInitParameter(BooleanWebContextInitParameter.EnableLazyBeanValidation, false);
192 Verifier.setCurrentInstance(new Verifier());
193 }
194 initScripting();
195 configManager.initialize(context);
196 initConfigMonitoring(context);
197
198 // Step 7, verify that all the configured factories are available
199 // and optionall that configured objects can be created.
200 Verifier v = Verifier.getCurrentInstance();
201 if (v != null && !v.isApplicationValid()) {
202 if (LOGGER.isLoggable(Level.SEVERE)) {
203 LOGGER.severe("jsf.config.verifyobjects.failures_detected");
204 StringBuilder sb = new StringBuilder(128);
205 for (String m : v.getMessages()) {
206 sb.append(m).append('\n');
207 }
208 LOGGER.severe(sb.toString());
209 }
210 }
211 registerELResolverAndListenerWithJsp(context, false);
212 ApplicationAssociate associate =
213 ApplicationAssociate.getInstance(context);
214 if (associate != null) {
215 associate.setContextName(getServletContextIdentifier(context));
216 }
217 RenderKitUtils.loadSunJsfJs(initContext.getExternalContext());
218
219 } finally {
220 Verifier.setCurrentInstance(null);
221 initContext.release();
222 LOGGER.log(Level.FINE,
223 "jsf.config.listener.version.complete");
224 if (timer != null) {
225 timer.stopTiming();
226 timer.logResult("Initialization of context " +
227 getServletContextIdentifier(context));
228 }
229 }
230 }
231
232
233 public void contextDestroyed(ServletContextEvent sce) {
234 webAppListener.contextDestroyed(sce);
235 webAppListener = null;
236 ServletContext context = sce.getServletContext();
237 GroovyHelper helper = GroovyHelper.getCurrentInstance(context);
238 if (helper != null) {
239 helper.setClassLoader();
240 }
241
242 LOGGER.log(Level.FINE,
243 "ConfigureListener.contextDestroyed({0})",
244 context.getServletContextName());
245
246 try {
247 // Release any allocated application resources
248 FactoryFinder.releaseFactories();
249 //monitor.cancel(true);
250 //webResourcePool.purge();
251 webResourcePool.shutdown();
252 } finally {
253 FacesContext initContext = new InitFacesContext(context);
254 ApplicationAssociate
255 .clearInstance(initContext.getExternalContext());
256 ApplicationAssociate.setCurrentInstance(null);
257 // Release the initialization mark on this web application
258 ConfigManager.getInstance().destory(context);
259 initContext.release();
260 ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader());
261 WebConfiguration.clear(context);
262 }
263
264 }
265
266
267 // ------------------------------------- Methods from ServletRequestListener
268
269
270 public void requestDestroyed(ServletRequestEvent event) {
271 if (webAppListener != null) {
272 webAppListener.requestDestroyed(event);
273 }
274 }
275
276
277 public void requestInitialized(ServletRequestEvent event) {
278 if (webAppListener != null) {
279 webAppListener.requestInitialized(event);
280 }
281 }
282
283
284 // ----------------------------------------- Methods from HttpSessionListener
285
286
287 public void sessionCreated(HttpSessionEvent event) {
288 if (webAppListener != null) {
289 webAppListener.sessionCreated(event);
290 }
291 }
292
293
294 public void sessionDestroyed(HttpSessionEvent event) {
295 if (webAppListener != null) {
296 webAppListener.sessionDestroyed(event);
297 }
298 }
299
300
301 // ---------------------------- Methods from ServletRequestAttributeListener
302
303
304 public void attributeAdded(ServletRequestAttributeEvent event) {
305 // ignored
306 }
307
308
309 public void attributeRemoved(ServletRequestAttributeEvent event) {
310 if (webAppListener != null) {
311 webAppListener.attributeRemoved(event);
312 }
313 }
314
315
316 public void attributeReplaced(ServletRequestAttributeEvent event) {
317 if (webAppListener != null) {
318 webAppListener.attributeReplaced(event);
319 }
320 }
321
322
323 // ------------------------------- Methods from HttpSessionAttributeListener
324
325
326 public void attributeAdded(HttpSessionBindingEvent event) {
327 // ignored
328 }
329
330
331 public void attributeRemoved(HttpSessionBindingEvent event) {
332 if (webAppListener != null) {
333 webAppListener.attributeRemoved(event);
334 }
335 }
336
337
338 public void attributeReplaced(HttpSessionBindingEvent event) {
339 if (webAppListener != null) {
340 webAppListener.attributeReplaced(event);
341 }
342 }
343
344
345 // ---------------------------- Methods from ServletContextAttributeListener
346
347
348 public void attributeAdded(ServletContextAttributeEvent event) {
349 // ignored
350 }
351
352 public void attributeRemoved(ServletContextAttributeEvent event) {
353 if (webAppListener != null) {
354 webAppListener.attributeRemoved(event);
355 }
356 }
357
358 public void attributeReplaced(ServletContextAttributeEvent event) {
359 if (webAppListener != null) {
360 webAppListener.attributeReplaced(event);
361 }
362 }
363
364
365 // --------------------------------------------------------- Private Methods
366
367
368 private void initConfigMonitoring(ServletContext context) {
369
370 //noinspection unchecked
371 List<URL> webURLs =
372 (List<URL>) context.getAttribute("com.sun.faces.webresources");
373 if (isDevModeEnabled() && webURLs != null && !webURLs.isEmpty()) {
374 webResourcePool.scheduleAtFixedRate(new WebConfigResourceMonitor(context, webURLs),
375 2000,
376 2000,
377 TimeUnit.MILLISECONDS);
378 }
379 context.removeAttribute("com.sun.faces.webresources");
380
381 }
382
383 private void initScripting() {
384 //if (isDevModeEnabled()) {
385 GroovyHelper helper = GroovyHelperFactory.createHelper();
386 if (helper != null) {
387 helper.setClassLoader();
388 }
389 //}
390 }
391
392
393 private boolean isDevModeEnabled() {
394
395 return webConfig.isOptionEnabled(BooleanWebContextInitParameter.DevelopmentMode);
396
397 }
398
399
400 /**
401 * This method will be invoked {@link WebConfigResourceMonitor} when
402 * changes to any of the faces-config.xml files included in WEB-INF
403 * are modified.
404 */
405 private void reload(ServletContext sc) {
406
407 if (LOGGER.isLoggable(Level.INFO)) {
408 LOGGER.log(Level.INFO,
409 "Reloading JSF configuration for context {0}",
410 getServletContextIdentifier(sc));
411 }
412 GroovyHelper helper = GroovyHelper.getCurrentInstance();
413 if (helper != null) {
414 helper.setClassLoader();
415 }
416 // tear down the application
417 try {
418 List<HttpSession> sessions = webAppListener.getActiveSessions();
419 if (sessions != null) {
420 for (HttpSession session : sessions) {
421 if (LOGGER.isLoggable(Level.INFO)) {
422 LOGGER.log(Level.INFO,
423 "Invalidating Session {0}",
424 session.getId());
425 }
426 session.invalidate();
427 }
428 }
429 ApplicationAssociate associate = ApplicationAssociate.getInstance(sc);
430 if (associate != null) {
431 BeanManager manager = associate.getBeanManager();
432 for (Map.Entry<String,BeanBuilder> entry : manager.getRegisteredBeans().entrySet()) {
433 String name = entry.getKey();
434 BeanBuilder bean = entry.getValue();
435 if (bean.getScope() == ELUtils.Scope.APPLICATION) {
436 if (LOGGER.isLoggable(Level.INFO)) {
437 LOGGER.log(Level.INFO,
438 "Removing application scoped managed bean: {0}",
439 name);
440 }
441 sc.removeAttribute(name);
442 }
443
444 }
445 }
446 // Release any allocated application resources
447 FactoryFinder.releaseFactories();
448 } catch (Exception e) {
449 e.printStackTrace();
450 } finally {
451 FacesContext initContext = new InitFacesContext(sc);
452 ApplicationAssociate
453 .clearInstance(initContext.getExternalContext());
454 ApplicationAssociate.setCurrentInstance(null);
455 // Release the initialization mark on this web application
456 ConfigManager.getInstance().destory(sc);
457 initContext.release();
458 ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader());
459 WebConfiguration.clear(sc);
460 }
461
462 // bring the application back up, avoid re-registration of certain JSP
463 // artifacts. No verification will be performed either to make this
464 // light weight.
465
466 // init a new WebAppLifecycleListener so that the cached ApplicationAssociate
467 // is removed.
468 webAppListener = new WebappLifecycleListener(sc);
469
470 FacesContext initContext = new InitFacesContext(sc);
471 ReflectionUtils
472 .initCache(Thread.currentThread().getContextClassLoader());
473
474 try {
475 ConfigManager configManager = ConfigManager.getInstance();
476 configManager.initialize(sc);
477
478
479 registerELResolverAndListenerWithJsp(sc, true);
480 ApplicationAssociate associate =
481 ApplicationAssociate.getInstance(sc);
482 if (associate != null) {
483 associate.setContextName(getServletContextIdentifier(sc));
484 }
485 RenderKitUtils.loadSunJsfJs(initContext.getExternalContext());
486 } catch (Exception e) {
487 e.printStackTrace();
488 } finally {
489 initContext.release();
490 }
491
492 if (LOGGER.isLoggable(Level.INFO)) {
493 LOGGER.log(Level.INFO,
494 "Reload complete.",
495 getServletContextIdentifier(sc));
496 }
497
498 }
499
500
501 private static String getServletContextIdentifier(ServletContext context) {
502 if (context.getMajorVersion() == 2 && context.getMinorVersion() < 5) {
503 return context.getServletContextName();
504 } else {
505 return context.getContextPath();
506 }
507 }
508
509
510
511 private static boolean isJspTwoOne() {
512
513 // The following try/catch is a hack to work around
514 // a bug in Tomcat 6 where JspFactory.getDefaultFactory() will
515 // return null unless JspRuntimeContext has been loaded.
516 try {
517 Class.forName("org.apache.jasper.compiler.JspRuntimeContext");
518 } catch (ClassNotFoundException ignored) {
519 // ignored
520 }
521
522 if (JspFactory.getDefaultFactory() == null) {
523 return false;
524 }
525 try {
526 JspFactory.class.getMethod("getJspApplicationContext",
527 ServletContext.class);
528 } catch (NoSuchMethodException nsme) {
529 return false;
530 }
531 return true;
532
533 }
534
535 public void registerELResolverAndListenerWithJsp(ServletContext context, boolean reloaded) {
536
537 if (webConfig.isSet(WebContextInitParameter.ExpressionFactory) ||
538 !isJspTwoOne()) {
539
540 // first try to load a factory defined in web.xml
541 if (!installExpressionFactory(context,
542 webConfig.getOptionValue(
543 WebContextInitParameter.ExpressionFactory))) {
544
545 throw new ConfigurationException(
546 MessageUtils.getExceptionMessageString(
547 MessageUtils.INCORRECT_JSP_VERSION_ID,
548 WebContextInitParameter.ExpressionFactory.getDefaultValue(),
549 WebContextInitParameter.ExpressionFactory.getQualifiedName()));
550
551 }
552
553 } else {
554
555 // JSP 2.1 specific check
556 if (JspFactory.getDefaultFactory().getJspApplicationContext(context) == null) {
557 return;
558 }
559
560 // register an empty resolver for now. It will be populated after the
561 // first request is serviced.
562 CompositeELResolver compositeELResolverForJsp =
563 new FacesCompositeELResolver(FacesCompositeELResolver.ELResolverChainType.JSP);
564 ApplicationAssociate associate =
565 ApplicationAssociate.getInstance(context);
566 if (associate != null) {
567 associate.setFacesELResolverForJsp(compositeELResolverForJsp);
568 }
569
570 // get JspApplicationContext.
571 JspApplicationContext jspAppContext = JspFactory.getDefaultFactory()
572 .getJspApplicationContext(context);
573
574 // cache the ExpressionFactory instance in ApplicationAssociate
575 if (associate != null) {
576 associate.setExpressionFactory(jspAppContext.getExpressionFactory());
577 }
578
579 // register compositeELResolver with JSP
580 try {
581 jspAppContext.addELResolver(compositeELResolverForJsp);
582 }
583 catch (IllegalStateException e) {
584 if (!Util.isUnitTestModeEnabled() && !reloaded) {
585 throw e;
586 }
587 }
588
589 // register JSF ELContextListenerImpl with Jsp
590 ELContextListenerImpl elContextListener = new ELContextListenerImpl();
591 jspAppContext.addELContextListener(elContextListener);
592 }
593 }
594
595 private boolean installExpressionFactory(ServletContext sc,
596 String elFactoryType){
597
598 if (elFactoryType == null) {
599 return false;
600 }
601 try {
602 ExpressionFactory factory = (ExpressionFactory)
603 Class.forName(elFactoryType).newInstance();
604 ApplicationAssociate associate =
605 ApplicationAssociate.getInstance(sc);
606 if (associate != null) {
607 associate.setExpressionFactory(factory);
608 }
609 return true;
610 } catch (Exception e) {
611 LOGGER.severe(MessageFormat.format("Unable to instantiate ExpressionFactory ''{0}''",
612 elFactoryType));
613 return false;
614 }
615
616 }
617
618 // ----------------------------------------------------------- Inner classes
619
620
621 /**
622 * <p>Processes a web application's deployment descriptor looking
623 * for a reference to <code>javax.faces.webapp.FacesServlet</code>.</p>
624 */
625 private static class WebXmlProcessor {
626
627 private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
628
629 private boolean facesServletPresent;
630
631
632 /**
633 * <p>When instantiated, the web.xml of the current application
634 * will be scanned looking for a references to the
635 * <code>FacesServlet</code>. <code>isFacesServletPresent()</code>
636 * will return the appropriate value based on the scan.</p>
637 * @param context the <code>ServletContext</code> for the application
638 * of interest
639 */
640 WebXmlProcessor(ServletContext context) {
641
642 if (context != null) {
643 scanForFacesServlet(context);
644 }
645
646 } // END WebXmlProcessor
647
648
649 /**
650 * @return <code>true</code> if the <code>WebXmlProcessor</code>
651 * detected a <code>FacesServlet</code> entry, otherwise return
652 * <code>false</code>.</p>
653 */
654 boolean isFacesServletPresent() {
655
656 return facesServletPresent;
657
658 } // END isFacesServletPresent
659
660
661 /**
662 * <p>Parse the web.xml for the current application and scan
663 * for a FacesServlet entry, if found, set the
664 * <code>facesServletPresent</code> property to true.
665 * @param context the ServletContext instance for this application
666 */
667 private void scanForFacesServlet(ServletContext context) {
668
669 SAXParserFactory factory = getConfiguredFactory();
670 try {
671 SAXParser parser = factory.newSAXParser();
672 parser.parse(context.getResourceAsStream(WEB_XML_PATH),
673 new WebXmlHandler());
674 } catch (Exception e) {
675 // This probably won't happen since the container would
676 // catch it before we would, but, if we catch an exception
677 // processing the web.xml, set facesServletFound to true to
678 // default to our previous behavior of processing the faces
679 // configuration.
680 if (LOGGER.isLoggable(Level.WARNING)) {
681 LOGGER.log(Level.WARNING,
682 MessageFormat.format("Unable to process deployment descriptor for context ''{0}''",
683 getServletContextIdentifier(context)));
684 }
685 facesServletPresent = true;
686 }
687
688 } // END scanForFacesServlet
689
690 /**
691 * <p>Return a <code>SAXParserFactory</code> instance that is
692 * non-validating and is namespace aware.</p>
693 * @return configured <code>SAXParserFactory</code>
694 */
695 private SAXParserFactory getConfiguredFactory() {
696
697 SAXParserFactory factory = SAXParserFactory.newInstance();
698 factory.setValidating(false);
699 factory.setNamespaceAware(true);
700 return factory;
701
702 } // END getConfiguredFactory
703
704
705 /**
706 * <p>A simple SAX handler to process the elements of interested
707 * within a web application's deployment descriptor.</p>
708 */
709 private class WebXmlHandler extends DefaultHandler {
710
711 private static final String SERVLET_CLASS = "servlet-class";
712 private static final String FACES_SERVLET =
713 "javax.faces.webapp.FacesServlet";
714
715 private boolean servletClassFound;
716 @SuppressWarnings({"StringBufferField"})
717 private StringBuffer content;
718
719 public InputSource resolveEntity(String publicId, String systemId)
720 throws SAXException {
721
722 return new InputSource(new StringReader(""));
723
724 } // END resolveEntity
725
726
727 public void startElement(String uri, String localName,
728 String qName, Attributes attributes)
729 throws SAXException {
730
731 if (!facesServletPresent) {
732 if (SERVLET_CLASS.equals(localName)) {
733 servletClassFound = true;
734 //noinspection StringBufferWithoutInitialCapacity
735 content = new StringBuffer();
736 } else {
737 servletClassFound = false;
738 }
739 }
740
741 } // END startElement
742
743
744 public void characters(char[] ch, int start, int length)
745 throws SAXException {
746
747 if (servletClassFound && !facesServletPresent) {
748 content.append(ch, start, length);
749 }
750
751 } // END characters
752
753
754 public void endElement(String uri, String localName, String qName)
755 throws SAXException {
756
757 if (servletClassFound && !facesServletPresent) {
758 if (FACES_SERVLET.equals(content.toString().trim())) {
759 facesServletPresent = true;
760 }
761 }
762
763 } // END endElement
764
765 } // END WebXmlHandler
766
767 } // END WebXmlProcessor
768
769
770 private class WebConfigResourceMonitor implements Runnable {
771
772 private List<Monitor> monitors;
773 private ServletContext sc;
774
775 // -------------------------------------------------------- Constructors
776
777
778 public WebConfigResourceMonitor(ServletContext sc, List<URL> urls) {
779
780 assert (urls != null);
781 this.sc = sc;
782 for (URL url : urls) {
783 if (monitors == null) {
784 monitors = new ArrayList<Monitor>(urls.size());
785 }
786 monitors.add(new Monitor(url));
787 }
788
789 }
790
791
792 // ----------------------------------------------- Methods from Runnable
793
794 /**
795 * PENDING javadocs
796 */
797 public void run() {
798
799 assert (monitors != null);
800 boolean reloaded = false;
801 for (Monitor m : monitors) {
802 if (m.hasBeenModified()) {
803 if (!reloaded) {
804 reloaded = true;
805 }
806 }
807 }
808 if (reloaded) {
809 reload(sc);
810 }
811
812 }
813
814
815 // ------------------------------------------------------- Inner Classes
816
817
818 private class Monitor {
819
820 private URL url;
821 private long timestamp = -1;
822
823 // ---------------------------------------------------- Constructors
824
825
826 Monitor(URL url) {
827
828 this.url = url;
829 this.timestamp = getLastModified();
830 LOGGER.log(Level.INFO,
831 "Monitoring {0} for modifications",
832 url.toExternalForm());
833
834 }
835
836
837 // ----------------------------------------- Package Private Methods
838
839
840 boolean hasBeenModified() {
841 long temp = getLastModified();
842 if (timestamp < temp) {
843 timestamp = temp;
844 LOGGER.log(Level.INFO,
845 "{0} changed!",
846 url.toExternalForm());
847 return true;
848 }
849 return false;
850
851 }
852
853
854 // ------------------------------------------------- Private Methods
855
856
857 private long getLastModified() {
858
859 InputStream in = null;
860 try {
861 URLConnection conn = url.openConnection();
862 conn.connect();
863 in = conn.getInputStream();
864 return conn.getLastModified();
865 } catch (IOException ioe) {
866 if (LOGGER.isLoggable(Level.SEVERE)) {
867 LOGGER.log(Level.SEVERE,
868 "Unable to check JAR timestamp.",
869 ioe);
870 }
871 return this.timestamp;
872 } finally {
873 if (in != null) {
874 try {
875 in.close();
876 } catch (IOException ignored) {
877 }
878 }
879 }
880
881 }
882
883 } // END Monitor
884
885 } // END WebConfigResourceMonitor
886
887 }
888