2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.tiger.view.faces.LifecycleListener2
 
Classes in this File Line Coverage Branch Coverage Complexity
LifecycleListener2
100%
382/382
N/A
0
LifecycleListener2$1
100%
1/1
N/A
0
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.shale.tiger.view.faces;
 19  
 
 20  
 import java.io.IOException;
 21  
 import java.lang.reflect.Field;
 22  
 import java.lang.reflect.InvocationTargetException;
 23  
 import java.lang.reflect.Method;
 24  
 import java.net.JarURLConnection;
 25  
 import java.net.MalformedURLException;
 26  
 import java.net.URL;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Enumeration;
 29  
 import java.util.HashMap;
 30  
 import java.util.Iterator;
 31  
 import java.util.List;
 32  
 import java.util.Map;
 33  
 import java.util.Set;
 34  
 import java.util.jar.JarEntry;
 35  
 import java.util.jar.JarFile;
 36  
 import javax.faces.FacesException;
 37  
 import javax.faces.FactoryFinder;
 38  
 import javax.faces.application.Application;
 39  
 import javax.faces.application.ApplicationFactory;
 40  
 import javax.faces.context.FacesContext;
 41  
 import javax.faces.event.PhaseListener;
 42  
 import javax.faces.lifecycle.Lifecycle;
 43  
 import javax.faces.lifecycle.LifecycleFactory;
 44  
 import javax.faces.render.RenderKit;
 45  
 import javax.faces.render.RenderKitFactory;
 46  
 import javax.faces.render.Renderer;
 47  
 import javax.servlet.ServletContext;
 48  
 import javax.servlet.ServletContextAttributeEvent;
 49  
 import javax.servlet.ServletContextEvent;
 50  
 import javax.servlet.ServletRequest;
 51  
 import javax.servlet.ServletRequestAttributeEvent;
 52  
 import javax.servlet.ServletRequestEvent;
 53  
 import javax.servlet.http.HttpSessionBindingEvent;
 54  
 import javax.servlet.http.HttpSessionEvent;
 55  
 import org.apache.commons.logging.Log;
 56  
 import org.apache.commons.logging.LogFactory;
 57  
 import org.apache.shale.tiger.config.FacesConfigConfig;
 58  
 import org.apache.shale.tiger.config.FacesConfigParser;
 59  
 import org.apache.shale.tiger.managed.Bean;
 60  
 import org.apache.shale.tiger.managed.Property;
 61  
 import org.apache.shale.tiger.managed.Value;
 62  
 import org.apache.shale.tiger.managed.config.ManagedBeanConfig;
 63  
 import org.apache.shale.tiger.managed.config.ManagedPropertyConfig;
 64  
 import org.apache.shale.tiger.register.FacesComponent;
 65  
 import org.apache.shale.tiger.register.FacesConverter;
 66  
 import org.apache.shale.tiger.register.FacesPhaseListener;
 67  
 import org.apache.shale.tiger.register.FacesRenderer;
 68  
 import org.apache.shale.tiger.register.FacesValidator;
 69  
 import org.apache.shale.tiger.register.faces.PhaseListenerAdapter;
 70  
 import org.apache.shale.tiger.view.Activate;
 71  
 import org.apache.shale.tiger.view.Destroy;
 72  
 import org.apache.shale.tiger.view.Init;
 73  
 import org.apache.shale.tiger.view.Passivate;
 74  
 import org.apache.shale.tiger.view.Preprocess;
 75  
 import org.apache.shale.tiger.view.Prerender;
 76  
 import org.apache.shale.tiger.view.Request;
 77  
 import org.apache.shale.tiger.view.Session;
 78  
 import org.apache.shale.tiger.view.View;
 79  
 import org.apache.shale.util.Messages;
 80  
 import org.apache.shale.view.AbstractApplicationBean;
 81  
 import org.apache.shale.view.AbstractRequestBean;
 82  
 import org.apache.shale.view.AbstractSessionBean;
 83  
 import org.apache.shale.view.ViewController;
 84  
 import org.apache.shale.view.faces.FacesConstants;
 85  
 import org.apache.shale.view.faces.LifecycleListener;
 86  
 import org.xml.sax.SAXException;
 87  
 
 88  
 /**
 89  
  * <p>Specialized version of
 90  
  * <code>org.apache.shale.view.faces.LifecycleListener</code>
 91  
  * that implements callbacks to methods tagged by appropriate annotations,
 92  
  * rather than requiring the containing classes to implement a particular
 93  
  * interface or extend a particular subclass.</p>
 94  
  *
 95  
  * <p><strong>IMPLEMENTATION NOTE:</strong> The standard LifecycleListener
 96  
  * instance will <em>delegate</em> to methods of this class after
 97  
  * performing its own appropriate processing.  Therefore, implementation
 98  
  * methods must <strong>NOT</strong> call their superclass counterparts.
 99  
  * Doing so will cause any infinite recursion and ultimately a stack
 100  
  * overflow error.</p>
 101  
  *
 102  
  * $Id: LifecycleListener2.java 489966 2006-12-24 01:43:42Z craigmcc $
 103  
  *
 104  
  * @since 1.0.3
 105  
  */
 106  
 public class LifecycleListener2 extends LifecycleListener {
 107  
 
 108  
 
 109  
     // ------------------------------------------------------------- Constructor
 110  
 
 111  
 
 112  
     /**
 113  
      * <p>Create a new lifecycle listener.</p>
 114  
      */
 115  24
     public LifecycleListener2() {
 116  24
     }
 117  
 
 118  
 
 119  
     // ------------------------------------------------------ Manifest Constants
 120  
 
 121  
 
 122  
     /**
 123  
      * <p> Servlet context init parameter which defines which packages to scan
 124  
      * for beans.</p>
 125  
      */
 126  
     public static final String SCAN_PACKAGES =
 127  
             "org.apache.shale.tiger.SCAN_PACKAGES";
 128  
 
 129  
 
 130  
     /**
 131  
      * <p>Application scope attribute under which a configured
 132  
      * {@link FacesConfigConfig} bean will be stored, containing
 133  
      * information parsed from the relevant <code>faces-config.xml</code>
 134  
      * resource(s) for this application.</p>
 135  
      */
 136  
     public static final String FACES_CONFIG_CONFIG =
 137  
             "org.apache.shale.tiger.FACES_CONFIG_CONFIG";
 138  
 
 139  
 
 140  
     /**
 141  
      * <p>Context relative path to the default <code>faces-config.xml</code>
 142  
      * resource for a JavaServer Faces application.</p>
 143  
      */
 144  
     private static final String FACES_CONFIG_DEFAULT =
 145  
             "/WEB-INF/faces-config.xml";
 146  
 
 147  
 
 148  
     /**
 149  
      * <p>Resource path used to acquire implicit resources buried
 150  
      * inside application JARs.</p>
 151  
      */
 152  
     private static final String FACES_CONFIG_IMPLICIT =
 153  
             "META-INF/faces-config.xml";
 154  
 
 155  
 
 156  
     /**
 157  
      * <p>Prefix path used to locate web application classes for this
 158  
      * web application.</p>
 159  
      */
 160  
     private static final String WEB_CLASSES_PREFIX = "/WEB-INF/classes/";
 161  
 
 162  
 
 163  
     /**
 164  
      * <p>Prefix path used to locate web application libraries for this
 165  
      * web application.</p>
 166  
      */
 167  
     private static final String WEB_LIB_PREFIX = "/WEB-INF/lib/";
 168  
 
 169  
 
 170  
     // ------------------------------------------------------ Instance Variables
 171  
 
 172  
 
 173  
     /**
 174  
      * <p>Parameter values array that passes no parameters.</p>
 175  
      */
 176  1
     private static final Object[] PARAMETERS = new Object[0];
 177  
 
 178  
 
 179  
     /**
 180  
      * <p>The <code>ServletContext</code> instance for this application.</p>
 181  
      */
 182  24
     private transient ServletContext servletContext = null;
 183  
 
 184  
 
 185  
     // ------------------------------------------ ServletContextListener Methods
 186  
 
 187  
 
 188  
     /**
 189  
      * <p>Respond to a context initialized event.  Forcibly replace
 190  
      * the managed bean services that are different when the Tiger
 191  
      * extensions are loaded.  Then, process the <code>faces-config.xml</code>
 192  
      * resources for this application in order to record the configuration
 193  
      * of managed beans.</p>
 194  
      *
 195  
      * @param event Event to be processed
 196  
      */
 197  
     public void contextInitialized(ServletContextEvent event) {
 198  
 
 199  18
         if (log().isInfoEnabled()) {
 200  18
             log().info(messages().getMessage("lifecycle.initialized"));
 201  
         }
 202  
 
 203  
         // Replace the ViewControllerCallbacks handler
 204  18
         servletContext = event.getServletContext();
 205  18
         servletContext.setAttribute(FacesConstants.VIEW_CALLBACKS,
 206  
                                     new ViewControllerCallbacks2());
 207  
 
 208  
         // Create an empty FacesConfigConfig instance and stash it as
 209  
         // an application scope attribute
 210  18
         FacesConfigConfig config = facesConfigConfig();
 211  18
         servletContext.setAttribute(FACES_CONFIG_CONFIG, config);
 212  
 
 213  
         // Determine if the JSF implementation has been initialized yet.
 214  
         // If it has, we will be able to register interesting classes
 215  
         // directly.  Otherwise, they will have to be queued up for
 216  
         // later processing.
 217  18
         Application application = null;
 218  
         try {
 219  18
             application = application();
 220  
         } catch (Exception e) {
 221  
             ; // Null means it is not initialized yet
 222  18
         }
 223  
 
 224  
         List<Class> classes;
 225  
 
 226  18
         String scanPackages = servletContext.getInitParameter(SCAN_PACKAGES);
 227  18
         if (scanPackages != null) {
 228  
             // Scan the classes configured by the scan_packages context parameter
 229  
             try {
 230  
                 classes = packageClasses(servletContext, scanPackages);
 231  
             } catch (ClassNotFoundException e) {
 232  
                 throw new FacesException(e);
 233  
             } catch (IOException e) {
 234  
                 throw new FacesException(e);
 235  
             }
 236  
         }
 237  
         else {
 238  
             // Scan the classes in /WEB-INF/classes for interesting annotations
 239  
             try {
 240  18
                 classes = webClasses(servletContext);
 241  
             } catch (ClassNotFoundException e) {
 242  
                 throw new FacesException(e);
 243  18
             }
 244  
         }
 245  
 
 246  
         try {
 247  18
             for (Class clazz : classes) {
 248  126
                 if (application != null) {
 249  126
                     registerClass(clazz, application);
 250  126
                 } else {
 251  
                     queueClass(clazz);
 252  
                 }
 253  126
                 scanClass(clazz, config);
 254  126
             }
 255  
         } catch (Exception e) {
 256  
             throw new FacesException(e);
 257  18
         }
 258  
 
 259  18
         if (scanPackages == null) {
 260  
             // Scan the classes in /WEB-INF/lib for interesting annotations
 261  18
             List<JarFile> archives = null;
 262  
             try {
 263  18
                 archives = webArchives(servletContext);
 264  18
                 for (JarFile archive : archives) {
 265  18
                     classes = archiveClasses(servletContext, archive);
 266  18
                     for (Class clazz : classes) {
 267  18
                         if (application != null) {
 268  18
                             registerClass(clazz, application);
 269  18
                         } else {
 270  
                             queueClass(clazz);
 271  
                         }
 272  18
                         scanClass(clazz, config);
 273  18
                     }
 274  18
                 }
 275  
             } catch (Exception e) {
 276  
                 throw new FacesException(e);
 277  18
             }
 278  
         }
 279  
 
 280  
         // Create a parser instance used to parse faces-config.xml resources
 281  18
         FacesConfigParser parser = facesConfigParser(config);
 282  18
         List<URL> resources = null;
 283  18
         URL url = null;
 284  
 
 285  
         // Parse the implicit faces-config.xml resources for this application
 286  
         try {
 287  18
             resources = implicitResources(servletContext);
 288  18
             for (URL resource : resources) {
 289  54
                 url = resource;
 290  54
                 parseResource(parser, resource);
 291  54
             }
 292  
         } catch (Exception e) {
 293  
             throw new FacesException(messages().getMessage("lifecycle.exception",
 294  
                                                            new Object[] { url.toExternalForm() }), e);
 295  18
         }
 296  
 
 297  
         // Parse the configured faces-config.xml resource(s) for this application
 298  
         try {
 299  18
             resources = explicitResources(servletContext);
 300  18
             for (URL resource : resources) {
 301  51
                 url = resource;
 302  51
                 parseResource(parser, resource);
 303  51
             }
 304  
         } catch (Exception e) {
 305  
             throw new FacesException(messages().getMessage("lifecycle.exception",
 306  
                                                            new Object[] { url.toExternalForm() }), e);
 307  18
         }
 308  
 
 309  
         // If not already processed, and if it actually exists, parse
 310  
         // the default faces-config.xml resource for this application
 311  
         try {
 312  18
             url = servletContext.getResource(FACES_CONFIG_DEFAULT);
 313  18
             if ((url != null) && !resources.contains(url)) {
 314  
                 parseResource(parser, url);
 315  
             }
 316  
         } catch (Exception e) {
 317  
             throw new FacesException(messages().getMessage("lifecycle.exception",
 318  
                                                            new Object[] { url.toExternalForm() }), e);
 319  18
         }
 320  
 
 321  18
         if (log().isInfoEnabled()) {
 322  18
             log().info(messages().getMessage("lifecycle.completed"));
 323  
         }
 324  
 
 325  18
     }
 326  
 
 327  
     /**
 328  
      * <p>Return a list of the classes defined within the given packages
 329  
      * If there are no such classes, a zero-length list will be returned.</p>
 330  
      *
 331  
      * @param scanPackages the package configuration
 332  
      *
 333  
      * @exception ClassNotFoundException if a located class cannot be loaded
 334  
      * @exception IOException if an input/output error occurs
 335  
      */
 336  
     private List<Class> packageClasses(ServletContext servletContext, String scanPackages)
 337  
       throws ClassNotFoundException, IOException {
 338  
 
 339  
         List<Class> list = new ArrayList<Class>();
 340  
 
 341  
         String[] scanPackageTokens =  scanPackages.split(",");
 342  
         for (String scanPackageToken : scanPackageTokens)
 343  
         {
 344  
             if (scanPackageToken.toLowerCase().endsWith(".jar"))
 345  
             {
 346  
                 URL jarResource = servletContext.getResource(WEB_LIB_PREFIX + scanPackageToken);
 347  
                 String jarURLString = "jar:" + jarResource.toString() + "!/";
 348  
                 URL url = new URL(jarURLString);
 349  
                 JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
 350  
 
 351  
                 list.addAll(archiveClasses(servletContext, jarFile));
 352  
             }
 353  
             else
 354  
             {
 355  
                 PackageInfo.getInstance().getClasses(list, scanPackageToken);
 356  
             }
 357  
         }
 358  
         return list;
 359  
     }
 360  
 
 361  
 
 362  
     /**
 363  
      * <p>Respond to a context destroyed event.  Clean up our allocated
 364  
      * application scope attributes.</p>
 365  
      *
 366  
      * @param event Event to be processed
 367  
      */
 368  
     public void contextDestroyed(ServletContextEvent event) {
 369  
 
 370  18
         if (log().isInfoEnabled()) {
 371  18
             log().info(messages().getMessage("lifecycle.destroyed"));
 372  
         }
 373  
 
 374  
         // Clean up our allocated application scope attributes
 375  18
         event.getServletContext().removeAttribute(FACES_CONFIG_CONFIG);
 376  
 
 377  18
     }
 378  
 
 379  
 
 380  
     // --------------------------------- ServletContextAttributeListener Methods
 381  
 
 382  
 
 383  
     /**
 384  
      * <p>Respond to an application scope attribute being added.  If the
 385  
      * value is an {@link AbstractApplicationBean}, call its
 386  
      * <code>init()</code> method.</p>
 387  
      *
 388  
      * @param event Event to be processed
 389  
      */
 390  
     public void attributeAdded(ServletContextAttributeEvent event) {
 391  
 
 392  8
         Object value = event.getValue();
 393  8
         if (value != null) {
 394  8
             fireApplicationInit(value);
 395  
         }
 396  
 
 397  8
     }
 398  
 
 399  
 
 400  
     /**
 401  
      * <p>Respond to an application scope attribute being replaced.
 402  
      * If the old value was an {@link AbstractApplicationBean}, call
 403  
      * its <code>destroy()</code> method.  If the new value is an
 404  
      * {@link AbstractApplicationBean}, call its <code>init()</code>
 405  
      * method.</p>
 406  
      *
 407  
      * @param event Event to be processed
 408  
      */
 409  
     public void attributeReplaced(ServletContextAttributeEvent event) {
 410  
 
 411  2
         Object value = event.getValue();
 412  2
         if (value != null) {
 413  2
             fireApplicationDestroy(value);
 414  
         }
 415  
 
 416  2
         value = event.getServletContext().getAttribute(event.getName());
 417  2
         if (value != null) {
 418  2
             fireApplicationInit(value);
 419  
         }
 420  
 
 421  2
     }
 422  
 
 423  
 
 424  
     /**
 425  
      * <p>Respond to an application scope attribute being removed.
 426  
      * If the old value was an {@link AbstractApplicationBean}, call
 427  
      * its <code>destroy()</code> method.</p>
 428  
      *
 429  
      * @param event Event to be processed
 430  
      */
 431  
     public void attributeRemoved(ServletContextAttributeEvent event) {
 432  
 
 433  8
         Object value = event.getValue();
 434  8
         if (value != null) {
 435  8
             fireApplicationDestroy(value);
 436  
         }
 437  
 
 438  8
     }
 439  
 
 440  
 
 441  
     // --------------------------------------------- HttpSessionListener Methods
 442  
 
 443  
 
 444  
     /**
 445  
      * <p>Respond to a session created event.  No special processing
 446  
      * is required.</p>
 447  
      *
 448  
      * @param event Event to be processed
 449  
      */
 450  
     public void sessionCreated(HttpSessionEvent event) {
 451  
 
 452  
         // No special processing is required
 453  
 
 454  
     }
 455  
 
 456  
 
 457  
     /**
 458  
      * <p>Respond to a session destroyed event.  No special
 459  
      * processing is required</p>
 460  
      *
 461  
      * @param event Event to be processed
 462  
      */
 463  
     public void sessionDestroyed(HttpSessionEvent event) {
 464  
 
 465  
         // No special processing is required
 466  
 
 467  
     }
 468  
 
 469  
 
 470  
     // ----------------------------------- HttpSessionActivationListener Methods
 471  
 
 472  
 
 473  
     /**
 474  
      * <p>Respond to a "session will passivate" event.  Notify all session
 475  
      * scope attributes that are {@link AbstractSessionBean}s.</p>
 476  
      *
 477  
      * @param event Event to be processed
 478  
      */
 479  
     public void sessionWillPassivate(HttpSessionEvent event) {
 480  
 
 481  
         Enumeration names = event.getSession().getAttributeNames();
 482  
         while (names.hasMoreElements()) {
 483  
             String name = (String) names.nextElement();
 484  
             Object value = event.getSession().getAttribute(name);
 485  
             if (value != null) {
 486  
                 fireSessionPassivate(value);
 487  
             }
 488  
         }
 489  
 
 490  
     }
 491  
 
 492  
 
 493  
     /**
 494  
      * <p>Respond to a "session did activate" event.  Notify all session
 495  
      * scope attributes that are {@link AbstractSessionBean}s.</p>
 496  
      *
 497  
      * @param event Event to be processed
 498  
      */
 499  
     public void sessionDidActivate(HttpSessionEvent event) {
 500  
 
 501  
         Enumeration names = event.getSession().getAttributeNames();
 502  
         while (names.hasMoreElements()) {
 503  
             String name = (String) names.nextElement();
 504  
             Object value = event.getSession().getAttribute(name);
 505  
             if (value != null) {
 506  
                 fireSessionActivate(value);
 507  
             }
 508  
         }
 509  
 
 510  
     }
 511  
 
 512  
 
 513  
     // ------------------------------------ HttpSessionAttributeListener Methods
 514  
 
 515  
 
 516  
     /**
 517  
      * <p>Respond to a session scope attribute being added.  If the
 518  
      * value is an {@link AbstractSessionBean}, call its
 519  
      * <code>init()</code> method.</p>
 520  
      *
 521  
      * @param event Event to be processed
 522  
      */
 523  
     public void attributeAdded(HttpSessionBindingEvent event) {
 524  
 
 525  2
         Object value = event.getValue();
 526  2
         if (value != null) {
 527  2
             fireSessionInit(value);
 528  
         }
 529  
 
 530  2
     }
 531  
 
 532  
 
 533  
     /**
 534  
      * <p>Respond to a session scope attribute being replaced.
 535  
      * If the old value was an {@link AbstractSessionBean}, call
 536  
      * its <code>destroy()</code> method.  If the new value is an
 537  
      * {@link AbstractSessionBean}, call its <code>init()</code>
 538  
      * method.</p>
 539  
      *
 540  
      * @param event Event to be processed
 541  
      */
 542  
     public void attributeReplaced(HttpSessionBindingEvent event) {
 543  
 
 544  2
         Object value = event.getValue();
 545  2
         if (value != null) {
 546  2
             fireSessionDestroy(value);
 547  
         }
 548  
 
 549  2
         value = event.getSession().getAttribute(event.getName());
 550  2
         if (value != null) {
 551  2
             fireSessionInit(value);
 552  
         }
 553  
 
 554  2
     }
 555  
 
 556  
 
 557  
     /**
 558  
      * <p>Respond to a session scope attribute being removed.
 559  
      * If the old value was an {@link AbstractSessionBean}, call
 560  
      * its <code>destroy()</code> method.</p>
 561  
      *
 562  
      * @param event Event to be processed
 563  
      */
 564  
     public void attributeRemoved(HttpSessionBindingEvent event) {
 565  
 
 566  2
         Object value = event.getValue();
 567  2
         if (value != null) {
 568  2
             fireSessionDestroy(value);
 569  
         }
 570  
 
 571  2
     }
 572  
 
 573  
 
 574  
     // ------------------------------------------ ServletRequestListener Methods
 575  
 
 576  
 
 577  
     /**
 578  
      * <p>Respond to a request created event.  If we have accumulated any
 579  
      * classes to register with our JSF implementation (but could not initially
 580  
      * because it was not initialized before we were), register them now.</p>
 581  
      *
 582  
      * @param event Event to be processed
 583  
      */
 584  
     public void requestInitialized(ServletRequestEvent event) {
 585  
 
 586  
         queueRegister();
 587  
 
 588  
     }
 589  
 
 590  
 
 591  
     /**
 592  
      * <p>Respond to a request destroyed event.  Cause any instance of
 593  
      * ViewController or AbstractRequestBean, plus any bean whose class
 594  
      * contains the appropriate annotations, to be removed (which will trigger
 595  
      * an attribute removed event).</p>
 596  
      *
 597  
      * @param event Event to be processed
 598  
      */
 599  
     public void requestDestroyed(ServletRequestEvent event) {
 600  
 
 601  
         // FIXME - may be more performant to just cause all request attributes
 602  
         // to be removed, because events will only be fired on relevant ones
 603  
         // --------------------------------------------------------------------
 604  
         // Remove any AbstractRequestBean or ViewController attributes,
 605  
         // as well as beans whose class contains the relevant annotations
 606  
         // which will trigger an attributeRemoved event
 607  
         List list = new ArrayList();
 608  
         ServletRequest request = event.getServletRequest();
 609  
         Enumeration names = request.getAttributeNames();
 610  
         while (names.hasMoreElements()) {
 611  
             String name = (String) names.nextElement();
 612  
             Object value = request.getAttribute(name);
 613  
             // Direct implementations of the relevant APIs are included
 614  
             if ((value instanceof AbstractRequestBean) || (value instanceof ViewController)) {
 615  
                 list.add(name);
 616  
                 continue;
 617  
             }
 618  
             // So are beans whose class implements the relevant annotations
 619  
             Class clazz = value.getClass();
 620  
             if ((clazz.getAnnotation(Request.class) != null)
 621  
              || (clazz.getAnnotation(View.class) != null)) {
 622  
                  list.add(name);
 623  
                  continue;
 624  
              }
 625  
         }
 626  
         Iterator keys = list.iterator();
 627  
         while (keys.hasNext()) {
 628  
             String key = (String) keys.next();
 629  
             event.getServletRequest().removeAttribute(key);
 630  
         }
 631  
 
 632  
         // No special processing is required
 633  
 
 634  
     }
 635  
 
 636  
 
 637  
     // --------------------------------- ServletRequestAttributeListener Methods
 638  
 
 639  
 
 640  
     /**
 641  
      * <p>Respond to a request scope attribute being added.  If the
 642  
      * value is an {@link AbstractRequestBean}, call its <code>init()</code> method.
 643  
      * </p>
 644  
      *
 645  
      * @param event Event to be processed
 646  
      */
 647  
     public void attributeAdded(ServletRequestAttributeEvent event) {
 648  
 
 649  2
         Object value = event.getValue();
 650  2
         if (value != null) {
 651  2
             fireRequestInit(value);
 652  
         }
 653  
 
 654  2
     }
 655  
 
 656  
 
 657  
     /**
 658  
      * <p>Respond to a request scope attribute being replaced.
 659  
      * If the old value was an {@link AbstractRequestBean},
 660  
      * call its <code>destroy()</code> method.  If the new value is an
 661  
      * {@link AbstractRequestBean}, call its <code>init()</code> method.</p>
 662  
      *
 663  
      * @param event Event to be processed
 664  
      */
 665  
     public void attributeReplaced(ServletRequestAttributeEvent event) {
 666  
 
 667  2
         Object value = event.getValue();
 668  2
         if (value != null) {
 669  2
             fireRequestDestroy(value);
 670  
         }
 671  
 
 672  2
         value = event.getServletRequest().getAttribute(event.getName());
 673  2
         if (value != null) {
 674  2
             fireRequestInit(value);
 675  
         }
 676  
 
 677  2
     }
 678  
 
 679  
 
 680  
     /**
 681  
      * <p>Respond to a request scope attribute being removed.
 682  
      * If the old value was an {@link AbstractRequestBean},
 683  
      * call its <code>destroy()</code> method.</p>
 684  
      *
 685  
      * @param event Event to be processed
 686  
      */
 687  
     public void attributeRemoved(ServletRequestAttributeEvent event) {
 688  
 
 689  2
         Object value = event.getValue();
 690  2
         if (value != null) {
 691  2
             fireRequestDestroy(value);
 692  
         }
 693  
 
 694  2
     }
 695  
 
 696  
 
 697  
     // ------------------------------------------------------- Protected Methods
 698  
 
 699  
 
 700  
     /**
 701  
      * <p>Fire a destroy event on an @{link AbstractApplicationBean}.</p>
 702  
      *
 703  
      * @param bean {@link AbstractApplicationBean} to fire event on
 704  
      */
 705  
     protected void fireApplicationDestroy(Object bean) {
 706  
 
 707  10
         if (bean instanceof AbstractApplicationBean) {
 708  2
             super.fireApplicationDestroy(bean);
 709  2
             return;
 710  
         }
 711  
 
 712  
         try {
 713  8
             Method method = method(bean, Destroy.class);
 714  8
             if (method != null) {
 715  2
                 method.invoke(bean, PARAMETERS);
 716  
             }
 717  
         } catch (InvocationTargetException e) {
 718  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 719  
         } catch (Exception e) {
 720  
             handleException(FacesContext.getCurrentInstance(), e);
 721  8
         }
 722  
 
 723  8
     }
 724  
 
 725  
 
 726  
     /**
 727  
      * <p>Fire an init event on an {@link AbstractApplicationBean}.</p>
 728  
      *
 729  
      * @param bean {@link AbstractApplicationBean} to fire event on
 730  
      */
 731  
     protected void fireApplicationInit(Object bean) {
 732  
 
 733  10
         if (bean instanceof AbstractApplicationBean) {
 734  2
             super.fireApplicationInit(bean);
 735  2
             return;
 736  
         }
 737  
 
 738  
         try {
 739  8
             Method method = method(bean, Init.class);
 740  8
             if (method != null) {
 741  2
                 method.invoke(bean, PARAMETERS);
 742  
             }
 743  
         } catch (InvocationTargetException e) {
 744  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 745  
         } catch (Exception e) {
 746  
             handleException(FacesContext.getCurrentInstance(), e);
 747  8
         }
 748  
 
 749  8
     }
 750  
 
 751  
 
 752  
     /**
 753  
      * <p>Fire a destroy event on an @{link AbstractRequestBean}.</p>
 754  
      *
 755  
      * @param bean {@link AbstractRequestBean} to fire event on
 756  
      */
 757  
     protected void fireRequestDestroy(Object bean) {
 758  
 
 759  4
         if ((bean instanceof AbstractRequestBean)
 760  
             || (bean instanceof ViewController)) {
 761  2
             super.fireRequestDestroy(bean);
 762  2
             return;
 763  
         }
 764  
 
 765  
         try {
 766  2
             Method method = method(bean, Destroy.class);
 767  2
             if (method != null) {
 768  2
                 method.invoke(bean, PARAMETERS);
 769  
             }
 770  
         } catch (InvocationTargetException e) {
 771  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 772  
         } catch (Exception e) {
 773  
             handleException(FacesContext.getCurrentInstance(), e);
 774  2
         }
 775  
 
 776  2
     }
 777  
 
 778  
 
 779  
     /**
 780  
      * <p>Fire an init event on an {@link AbstractRequestBean}.</p>
 781  
      *
 782  
      * @param bean {@link AbstractRequestBean} to fire event on
 783  
      */
 784  
     protected void fireRequestInit(Object bean) {
 785  
 
 786  4
         if ((bean instanceof AbstractRequestBean)
 787  
             || (bean instanceof ViewController)) {
 788  2
             super.fireRequestInit(bean);
 789  2
             return;
 790  
         }
 791  
 
 792  
         try {
 793  2
             Method method = method(bean, Init.class);
 794  2
             if (method != null) {
 795  2
                 method.invoke(bean, PARAMETERS);
 796  
             }
 797  
         } catch (InvocationTargetException e) {
 798  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 799  
         } catch (Exception e) {
 800  
             handleException(FacesContext.getCurrentInstance(), e);
 801  2
         }
 802  
 
 803  2
     }
 804  
 
 805  
 
 806  
     /**
 807  
      * <p>Fire an activate event on an @{link AbstractSessionBean}.</p>
 808  
      *
 809  
      * @param bean {@link AbstractSessionBean} to fire event on
 810  
      */
 811  
     protected void fireSessionActivate(Object bean) {
 812  
 
 813  
         if (bean instanceof AbstractSessionBean) {
 814  
             super.fireSessionActivate(bean);
 815  
             return;
 816  
         }
 817  
 
 818  
         try {
 819  
             Method method = method(bean, Activate.class);
 820  
             if (method != null) {
 821  
                 method.invoke(bean, PARAMETERS);
 822  
             }
 823  
         } catch (InvocationTargetException e) {
 824  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 825  
         } catch (Exception e) {
 826  
             handleException(FacesContext.getCurrentInstance(), e);
 827  
         }
 828  
 
 829  
     }
 830  
 
 831  
 
 832  
     /**
 833  
      * <p>Fire a destroy event on an @{link AbstractSessionBean}.</p>
 834  
      *
 835  
      * @param bean {@link AbstractSessionBean} to fire event on
 836  
      */
 837  
     protected void fireSessionDestroy(Object bean) {
 838  
 
 839  4
         if (bean instanceof AbstractSessionBean) {
 840  2
             super.fireSessionDestroy(bean);
 841  2
             return;
 842  
         }
 843  
 
 844  
         try {
 845  2
             Method method = method(bean, Destroy.class);
 846  2
             if (method != null) {
 847  2
                 method.invoke(bean, PARAMETERS);
 848  
             }
 849  
         } catch (InvocationTargetException e) {
 850  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 851  
         } catch (Exception e) {
 852  
             handleException(FacesContext.getCurrentInstance(), e);
 853  2
         }
 854  
 
 855  2
     }
 856  
 
 857  
 
 858  
     /**
 859  
      * <p>Fire an init event on an {@link AbstractSessionBean}.</p>
 860  
      *
 861  
      * @param bean {@link AbstractSessionBean} to fire event on
 862  
      */
 863  
     protected void fireSessionInit(Object bean) {
 864  
 
 865  4
         if (bean instanceof AbstractSessionBean) {
 866  2
             super.fireSessionInit(bean);
 867  2
             return;
 868  
         }
 869  
 
 870  
         try {
 871  2
             Method method = method(bean, Init.class);
 872  2
             if (method != null) {
 873  2
                 method.invoke(bean, PARAMETERS);
 874  
             }
 875  
         } catch (InvocationTargetException e) {
 876  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 877  
         } catch (Exception e) {
 878  
             handleException(FacesContext.getCurrentInstance(), e);
 879  2
         }
 880  
 
 881  2
     }
 882  
 
 883  
 
 884  
     /**
 885  
      * <p>Fire an passivate event on an @{link AbstractSessionBean}.</p>
 886  
      *
 887  
      * @param bean {@link AbstractSessionBean} to fire event on
 888  
      */
 889  
     protected void fireSessionPassivate(Object bean) {
 890  
 
 891  
         if (bean instanceof AbstractSessionBean) {
 892  
             super.fireSessionPassivate(bean);
 893  
             return;
 894  
         }
 895  
 
 896  
         try {
 897  
             Method method = method(bean, Passivate.class);
 898  
             if (method != null) {
 899  
                 method.invoke(bean, PARAMETERS);
 900  
             }
 901  
         } catch (InvocationTargetException e) {
 902  
             handleException(FacesContext.getCurrentInstance(), (Exception) e.getCause());
 903  
         } catch (Exception e) {
 904  
             handleException(FacesContext.getCurrentInstance(), e);
 905  
         }
 906  
 
 907  
     }
 908  
 
 909  
 
 910  
     // --------------------------------------------------------- Private Methods
 911  
 
 912  
 
 913  
     /**
 914  
      * <p>The Application instance for this application.</p>
 915  
      */
 916  24
     private Application application = null;
 917  
 
 918  
 
 919  
     /**
 920  
      * <p>Return the <code>Application</code> for this application.</p>
 921  
      */
 922  
     private Application application() {
 923  
 
 924  72
         if (application == null) {
 925  18
             application = ((ApplicationFactory) FactoryFinder.
 926  
               getFactory(FactoryFinder.APPLICATION_FACTORY)).getApplication();
 927  
         }
 928  72
         return application;
 929  
 
 930  
     }
 931  
 
 932  
 
 933  
     /**
 934  
      * <p>Return a list of classes to examine from the specified JAR archive.
 935  
      * If this archive has no classes in it, a zero-length list is returned.</p>
 936  
      *
 937  
      * @param context <code>ServletContext</code> instance for
 938  
      *  this application
 939  
      * @param jar <code>JarFile</code> for the archive to be scanned
 940  
      *
 941  
      * @exception ClassNotFoundException if a located class cannot be loaded
 942  
      */
 943  
     private List<Class> archiveClasses(ServletContext context, JarFile jar)
 944  
         throws ClassNotFoundException {
 945  
 
 946  
         // Accumulate and return a list of classes in this JAR file
 947  18
         List<Class> list = new ArrayList<Class>();
 948  18
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 949  18
         if (loader == null) {
 950  
             loader = this.getClass().getClassLoader();
 951  
         }
 952  18
         Enumeration<JarEntry> entries = jar.entries();
 953  180
         while (entries.hasMoreElements()) {
 954  162
             JarEntry entry = entries.nextElement();
 955  162
             if (entry.isDirectory()) {
 956  108
                 continue;                         // This is a directory
 957  
             }
 958  54
             String name = entry.getName();
 959  54
             if (name.startsWith("META-INF/")) {
 960  36
                 continue;                         // Attribute files
 961  
             }
 962  18
             if (!name.endsWith(".class")) {
 963  
                 continue;                         // This is not a class
 964  
             }
 965  18
             name = name.substring(0, name.length() - 6); // Trim ".class"
 966  18
             Class clazz = null;
 967  
             try {
 968  18
                 clazz = loader.loadClass(name.replace('/', '.'));
 969  
             } catch (NoClassDefFoundError e) {
 970  
                 ;  // Skip this class - we cannot analyze classes we cannot load
 971  
             } catch (Exception e) {
 972  
                 ;  // Skip this class - we cannot analyze classes we cannot load
 973  18
             }
 974  18
             if (clazz != null) {
 975  18
                 list.add(clazz);
 976  
             }
 977  18
         }
 978  18
         return list;
 979  
 
 980  
     }
 981  
 
 982  
 
 983  
     /**
 984  
      * <p>Return a list of URLs of <code>faces-config.xml</code>
 985  
      * resources for this web application that have been explicitly listed
 986  
      * in the appropriate context initialization parameter.  If there are no
 987  
      * such resources, a zero-length list is returned.</p>
 988  
      *
 989  
      * @param servletContext <code>ServletContext</code> instance for this
 990  
      *  application
 991  
      *
 992  
      * @exception MalformedURLException if a context relative resource path
 993  
      *  has incorrect syntax
 994  
      */
 995  
     private List<URL> explicitResources(ServletContext servletContext)
 996  
         throws MalformedURLException {
 997  
 
 998  
         // Create an empty list to contain our results
 999  18
         List<URL> list = new ArrayList<URL>();
 1000  
 
 1001  
         // If no initialization parameter was listed, return an empty list
 1002  18
         String resources = servletContext.getInitParameter("javax.faces.CONFIG_FILES");
 1003  18
         if (resources == null) {
 1004  
             return list;
 1005  
         }
 1006  
 
 1007  
         // Parse the comma-delimited list of context-relative resource paths
 1008  69
         while (resources.length() > 0) {
 1009  51
             int comma = resources.indexOf(',');
 1010  51
             if (comma < 0) {
 1011  18
                 resources = resources.trim();
 1012  18
                 if (resources.length() > 0) {
 1013  18
                     URL url = servletContext.getResource(resources);
 1014  18
                     if (url != null) {
 1015  18
                         list.add(url);
 1016  
                     }
 1017  
                 }
 1018  18
                 resources = "";
 1019  18
             } else {
 1020  33
                 URL url = servletContext.getResource(resources.substring(0, comma).trim());
 1021  33
                 if (url != null) {
 1022  33
                     list.add(url);
 1023  
                 }
 1024  33
                 resources = resources.substring(comma + 1);
 1025  
             }
 1026  51
         }
 1027  
 
 1028  
         // Return the completed list
 1029  18
         return list;
 1030  
 
 1031  
     }
 1032  
 
 1033  
     /**
 1034  
      * <p>Create and return an empty {@link FacesConfigConfig} bean that will
 1035  
      * be filled with information later on.</p>
 1036  
      */
 1037  
     private FacesConfigConfig facesConfigConfig() {
 1038  
 
 1039  18
         return new FacesConfigConfig();
 1040  
 
 1041  
     }
 1042  
 
 1043  
 
 1044  
     /**
 1045  
      * <p>Create and return a configured {@link FacesConfigParser} instance
 1046  
      * to be used for parsing <code>faces-config.xml</code> resources.  The
 1047  
      * caller will need to set the <code>resource</code> property on this
 1048  
      * instance before calling the <code>parse()</code> method.</p>
 1049  
      *
 1050  
      * @param config <code>FacesConfigBean</code> used to store the
 1051  
      *  information gathered while parsing configuration resources
 1052  
      */
 1053  
     private FacesConfigParser facesConfigParser(FacesConfigConfig config) {
 1054  
 
 1055  18
         FacesConfigParser parser = new FacesConfigParser();
 1056  18
         parser.setFacesConfig(config);
 1057  18
         parser.setValidating(true);
 1058  18
         return parser;
 1059  
 
 1060  
     }
 1061  
 
 1062  
 
 1063  
     /**
 1064  
      * <p>Return an array of all <code>Field</code>s reflecting declared
 1065  
      * fields in this class, or in any superclass other than
 1066  
      * <code>java.lang.Object</code>.</p>
 1067  
      *
 1068  
      * @param clazz Class to be analyzed
 1069  
      */
 1070  
     private Field[] fields(Class clazz) {
 1071  
 
 1072  54
         Map<String,Field> fields = new HashMap<String,Field>();
 1073  
         do {
 1074  522
             for (Field field : clazz.getDeclaredFields()) {
 1075  450
                 if (!fields.containsKey(field.getName())) {
 1076  450
                     fields.put(field.getName(), field);
 1077  
                 }
 1078  
             }
 1079  72
         } while ((clazz = clazz.getSuperclass()) != Object.class);
 1080  54
         return (Field[]) fields.values().toArray(new Field[fields.size()]);
 1081  
 
 1082  
     }
 1083  
 
 1084  
 
 1085  
 
 1086  
     /**
 1087  
      * <p>Return a list of URLs to implicit configuration resources
 1088  
      * embedded in this application.</p>
 1089  
      *
 1090  
      * @param servletContext <code>ServletContext</code> instance for this
 1091  
      *  application
 1092  
      *
 1093  
      * @exception IOException if an input/output error occurs
 1094  
      */
 1095  
     private List<URL> implicitResources(ServletContext servletContext)
 1096  
         throws IOException {
 1097  
 
 1098  18
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 1099  18
         if (loader == null) {
 1100  
             loader = this.getClass().getClassLoader();
 1101  
         }
 1102  18
         Enumeration items = loader.getResources(FACES_CONFIG_IMPLICIT);
 1103  18
         List<URL> list = new ArrayList<URL>();
 1104  72
         while (items.hasMoreElements()) {
 1105  54
             list.add((URL) items.nextElement());
 1106  54
         }
 1107  18
         return list;
 1108  
 
 1109  
     }
 1110  
 
 1111  
 
 1112  
     /**
 1113  
      * <p>The lifecycle instance for this application.</p>
 1114  
      */
 1115  24
     private Lifecycle lifecycle = null;
 1116  
 
 1117  
 
 1118  
     /**
 1119  
      * <p>Return the <code>Lifecycle</code> for this application.</p>
 1120  
      */
 1121  
     private Lifecycle lifecycle() {
 1122  
 
 1123  18
         if (lifecycle == null) {
 1124  18
             String lifecycleId = servletContext.getInitParameter("javax.faces.LIFECYCLE_ID");
 1125  18
             if (lifecycleId == null) {
 1126  18
                 lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
 1127  
             }
 1128  18
             lifecycle = ((LifecycleFactory) FactoryFinder.
 1129  
               getFactory(FactoryFinder.LIFECYCLE_FACTORY)).getLifecycle(lifecycleId);
 1130  
         }
 1131  18
         return lifecycle;
 1132  
 
 1133  
     }
 1134  
 
 1135  
 
 1136  
     /**
 1137  
      * <p>The <code>Log</code> instance we will be using.</p>
 1138  
      */
 1139  24
     private transient Log log = null;
 1140  
 
 1141  
 
 1142  
     /**
 1143  
      * <p>Return the <code>Log</code> instance to be used for this class,
 1144  
      * instantiating a new one if necessary.</p>
 1145  
      */
 1146  
     private Log log() {
 1147  
 
 1148  3198
         if (log == null) {
 1149  18
             log = LogFactory.getLog(LifecycleListener2.class);
 1150  
         }
 1151  3198
         return log;
 1152  
 
 1153  
     }
 1154  
 
 1155  
 
 1156  
     /**
 1157  
      * <p>The <code>Messages</code> instance we will be using.</p>
 1158  
      */
 1159  24
     private transient Messages messages = null;
 1160  
 
 1161  
 
 1162  
     /**
 1163  
      * <p>Return the <code>Messages</code> instance to be used for this class,
 1164  
      * instantiating a new one if necessary.</p>
 1165  
      */
 1166  
     private Messages messages() {
 1167  
 
 1168  54
         if (messages == null) {
 1169  18
             messages = new Messages("org.apache.shale.tiger.faces.Bundle",
 1170  
                                     Thread.currentThread().getContextClassLoader());
 1171  
         }
 1172  54
         return messages;
 1173  
 
 1174  
     }
 1175  
 
 1176  
 
 1177  
     /**
 1178  
      * <p>The set of method annotations for callbacks of interest.</p>
 1179  
      */
 1180  1
     private static final Class[] annotations =
 1181  
     { Init.class, Preprocess.class, Prerender.class, Destroy.class,
 1182  
       Activate.class, Passivate.class };
 1183  
 
 1184  
 
 1185  
 
 1186  
     /**
 1187  
      * <p>The set of class annotations for classes of interest.</p>
 1188  
      */
 1189  1
     private static final Class[] markers =
 1190  
     { View.class, Request.class, Session.class,
 1191  
       org.apache.shale.tiger.view.Application.class };
 1192  
 
 1193  
 
 1194  
 
 1195  
     /**
 1196  
      * <p>Data structure to maintain information about annotated
 1197  
      * methods.  In this map, the key is the Class being analyzed,
 1198  
      * and the value is an inner map.  In the inner map, the key
 1199  
      * is an Annotation class, and the value is the corresponding
 1200  
      * Method instance.</p>
 1201  
      */
 1202  24
     private transient Map<Class,Map<Class,Method>> maps =
 1203  
       new HashMap<Class,Map<Class,Method>>();
 1204  
 
 1205  
 
 1206  
     /**
 1207  
      * <p>Return the <code>Method</code> to be called for the specified
 1208  
      * annotation on the specified instance, if any.  If there is no such
 1209  
      * method, return <code>null</code>.</p>
 1210  
      *
 1211  
      * @param instance Instance on which callbacks will be performed
 1212  
      * @param annotation Annotation for which to return a method
 1213  
      */
 1214  
     private Method method(Object instance, Class annotation) {
 1215  
 
 1216  
 
 1217  
         // Does the underlying class implement a relevant class annotation?
 1218  
         // If not, exit early
 1219  24
         Class clazz = instance.getClass();
 1220  24
         boolean found = false;
 1221  96
         for (Class marker : markers) {
 1222  84
             if (clazz.getAnnotation(marker) != null) {
 1223  12
                 found = true;
 1224  12
                 break;
 1225  
             }
 1226  
         }
 1227  24
         if (!found) {
 1228  12
             return null;
 1229  
         }
 1230  
 
 1231  12
         synchronized (maps) {
 1232  
 
 1233  
             // If we have seen this Class already, simply return the
 1234  
             // previously located Method (if any)
 1235  12
             Map<Class,Method> map = maps.get(clazz);
 1236  12
             if (map != null) {
 1237  9
                 return map.get(annotation);
 1238  
             }
 1239  
 
 1240  
 
 1241  
             // Construct and cache a new Map identifying the
 1242  
             // methods of interest for these callbacks
 1243  3
             map = new HashMap<Class,Method>();
 1244  3
             Method[] methods = clazz.getMethods();
 1245  39
             for (Method method : methods) {
 1246  36
                 if (method.getParameterTypes().length > 0) {
 1247  9
                     continue;
 1248  
                 }
 1249  189
                 for (Class anno : annotations) {
 1250  162
                     if (method.getAnnotation(anno) != null) {
 1251  6
                         map.put(anno, method);
 1252  
                     }
 1253  
                 }
 1254  
             }
 1255  3
             maps.put(clazz, map);
 1256  3
             return map.get(annotation);
 1257  
 
 1258  
         }
 1259  
 
 1260  
     }
 1261  
 
 1262  
 
 1263  
     /**
 1264  
      * <p>Use the specified parser to parse the resource at the specified
 1265  
      * URL, which will accumulate additional information into the
 1266  
      * <code>FacesConfigConfig</code> instance configured on the parser.</p>
 1267  
      *
 1268  
      * @param parser FacesConfigParser instance to be used
 1269  
      * @param resource URL of the resource to be parsed
 1270  
      *
 1271  
      * @exception IOException if an input/output error occurs
 1272  
      * @exception SAXException if an XML parsing error occurs
 1273  
      */
 1274  
     private void parseResource(FacesConfigParser parser, URL resource)
 1275  
         throws IOException, SAXException {
 1276  
 
 1277  105
         if (log().isDebugEnabled()) {
 1278  105
             log().debug("Parsing faces-config.xml resource '"
 1279  
                         + resource.toExternalForm() + "'");
 1280  
         }
 1281  105
         parser.setResource(resource);
 1282  105
         parser.parse();
 1283  
 
 1284  105
     }
 1285  
 
 1286  
 
 1287  
 
 1288  
     /**
 1289  
      * <p>A list of classes that need to be registered with the JSF implementation
 1290  
      * after it has been started.</p>
 1291  
      */
 1292  24
     private List<Class> queue = new ArrayList<Class>();
 1293  
 
 1294  
 
 1295  
     /**
 1296  
      * <p>Queue the specified class to be registered (via <code>registerClass()</code>)
 1297  
      * at a later time.</p>
 1298  
      *
 1299  
      * @param clazz Class instance to be queued
 1300  
      */
 1301  
     private void queueClass(Class clazz) {
 1302  
         queue.add(clazz);
 1303  
     }
 1304  
 
 1305  
 
 1306  
     /**
 1307  
      * <p>Register any classes that have been queued.  This method is synchronzied
 1308  
      * because it is called from a request listener, and may therefore be subject
 1309  
      * to race conditions if multiple requests are received simultaneously.</p>
 1310  
      */
 1311  
     private synchronized void queueRegister() {
 1312  
 
 1313  
         if (queue == null) {
 1314  
             return;
 1315  
         }
 1316  
         for (Class clazz : queue) {
 1317  
             registerClass(clazz, application());
 1318  
         }
 1319  
         queue = null;
 1320  
 
 1321  
     }
 1322  
 
 1323  
 
 1324  
     /**
 1325  
      * <p>The render kit factory for this application.</p>
 1326  
      */
 1327  24
     private RenderKitFactory rkFactory = null;
 1328  
 
 1329  
 
 1330  
     /**
 1331  
      * <p>Return the <code>RenderKitFactory</code> for this application.</p>
 1332  
      */
 1333  
     private RenderKitFactory renderKitFactory() {
 1334  
 
 1335  18
         if (rkFactory == null) {
 1336  18
             rkFactory = (RenderKitFactory)
 1337  
               FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
 1338  
         }
 1339  18
         return rkFactory;
 1340  
 
 1341  
     }
 1342  
 
 1343  
 
 1344  
     /**
 1345  
      * <p>Register the specified class with the specified JavaServer Faces
 1346  
      * <code>Application</code> instance, based on annotations that the
 1347  
      * class is annotated with.</p>
 1348  
      *
 1349  
      * @param clazz Class to be registered
 1350  
      * @param application <code>Application</code> instance with which to
 1351  
      *  register this class
 1352  
      */
 1353  
     private void registerClass(Class clazz, Application application) {
 1354  
 
 1355  144
         if (log().isTraceEnabled()) {
 1356  144
             log().trace("registerClass(" + clazz.getName() + ")");
 1357  
         }
 1358  144
         FacesComponent comp = (FacesComponent) clazz.getAnnotation(FacesComponent.class);
 1359  144
         if (comp != null) {
 1360  18
             if (log().isTraceEnabled()) {
 1361  18
                 log().trace("addComponent(" + comp.value() + "," + clazz.getName() + ")");
 1362  
             }
 1363  18
             application().addComponent(comp.value(), clazz.getName());
 1364  
         }
 1365  
 
 1366  144
         FacesConverter conv = (FacesConverter) clazz.getAnnotation(FacesConverter.class);
 1367  144
         if (conv != null) {
 1368  18
             if (log().isTraceEnabled()) {
 1369  18
                 log().trace("addConverter(" + conv.value() + "," + clazz.getName() + ")");
 1370  
             }
 1371  18
             application().addConverter(conv.value(), clazz.getName());
 1372  
         }
 1373  
 
 1374  144
         FacesPhaseListener list = (FacesPhaseListener) clazz.getAnnotation(FacesPhaseListener.class);
 1375  144
         if (list != null) {
 1376  
             try {
 1377  18
                 Lifecycle lifecycle = lifecycle();
 1378  18
                 Object instance = clazz.newInstance();
 1379  18
                 if (instance instanceof PhaseListener) {
 1380  
                     lifecycle.addPhaseListener((PhaseListener) instance);
 1381  
                 } else {
 1382  18
                     lifecycle.addPhaseListener(new PhaseListenerAdapter(instance));
 1383  
                 }
 1384  
             } catch (FacesException e) {
 1385  
                 throw e;
 1386  
             } catch (Exception e) {
 1387  
                 throw new FacesException(e);
 1388  18
             }
 1389  
         }
 1390  
 
 1391  144
         FacesRenderer rend = (FacesRenderer) clazz.getAnnotation(FacesRenderer.class);
 1392  144
         if (rend != null) {
 1393  18
             String renderKitId = rend.renderKitId();
 1394  18
             if (renderKitId == null) {
 1395  
                 renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
 1396  
             }
 1397  18
             if (log().isTraceEnabled()) {
 1398  18
                 log().trace("addRenderer(" + renderKitId + ", " + rend.componentFamily()
 1399  
                   + ", " + rend.rendererType() + ", " + clazz.getName() + ")");
 1400  
             }
 1401  
             try {
 1402  18
                 RenderKit rk = renderKitFactory().getRenderKit(null, renderKitId);
 1403  18
                 rk.addRenderer(rend.componentFamily(), rend.rendererType(),
 1404  
                                (Renderer) clazz.newInstance());
 1405  
             } catch (Exception e) {
 1406  
                 throw new FacesException(e);
 1407  18
             }
 1408  
         }
 1409  
 
 1410  144
         FacesValidator val = (FacesValidator) clazz.getAnnotation(FacesValidator.class);
 1411  144
         if (val != null) {
 1412  18
             if (log().isTraceEnabled()) {
 1413  18
                 log().trace("addValidator(" + val.value() + "," + clazz.getName() + ")");
 1414  
             }
 1415  18
             application().addValidator(val.value(), clazz.getName());
 1416  
         }
 1417  
 
 1418  144
     }
 1419  
 
 1420  
 
 1421  
     /**
 1422  
      * <p>Scan the specified class for those that have annotations
 1423  
      * of interest, and construct appropriate configuration metadata attached
 1424  
      * to the specified {@link FacesConfigConfig} bean.</p>
 1425  
      *
 1426  
      * @param clazz Class to be scanned
 1427  
      * @param config {@link FacesConfigConfig} to be updated
 1428  
      */
 1429  
     private void scanClass(Class clazz, FacesConfigConfig config) {
 1430  
 
 1431  144
         if (log().isTraceEnabled()) {
 1432  144
             log().trace("Scanning class '" + clazz.getName() + "'");
 1433  
         }
 1434  144
         Bean bean = (Bean) clazz.getAnnotation(Bean.class);
 1435  144
         if (bean != null) {
 1436  54
             if (log().isDebugEnabled()) {
 1437  54
                 log().debug("Class '" + clazz.getName() + "' has an @Bean annotation");
 1438  
             }
 1439  54
             ManagedBeanConfig mbc = new ManagedBeanConfig();
 1440  54
             mbc.setName(bean.name());
 1441  54
             mbc.setType(clazz.getName());
 1442  1
             switch (bean.scope()) {
 1443  
                 case APPLICATION:
 1444  36
                     mbc.setScope("application");
 1445  36
                     break;
 1446  
                 case REQUEST:
 1447  
                     mbc.setScope("request");
 1448  
                     break;
 1449  
                 case SESSION:
 1450  18
                     mbc.setScope("session");
 1451  18
                     break;
 1452  
                 default:
 1453  
                     break;
 1454  
             }
 1455  54
             Field[] fields = fields(clazz);
 1456  504
             for (Field field : fields) {
 1457  450
                 if (log().isTraceEnabled()) {
 1458  450
                     log().trace("  Scanning field '" + field.getName() + "'");
 1459  
                 }
 1460  450
                 Property property = (Property) field.getAnnotation(Property.class);
 1461  450
                 if (property != null) {
 1462  144
                     if (log().isDebugEnabled()) {
 1463  144
                         log().debug("  Field '" + field.getName() + "' has a @Property annotation");
 1464  
                     }
 1465  144
                     ManagedPropertyConfig mpc = new ManagedPropertyConfig();
 1466  144
                     String name = property.name();
 1467  144
                     if ((name == null) || "".equals(name)) {
 1468  72
                         name = field.getName();
 1469  
                     }
 1470  144
                     mpc.setName(name);
 1471  144
                     mpc.setType(field.getType().getName()); // FIXME - primitives, arrays, etc.
 1472  144
                     mpc.setValue(property.value());
 1473  144
                     mbc.addProperty(mpc);
 1474  144
                     continue;
 1475  
                 }
 1476  
                 // Support deprecated @Value annotation as well
 1477  306
                 Value value = (Value) field.getAnnotation(Value.class);
 1478  306
                 if (value != null) {
 1479  306
                     if (log().isDebugEnabled()) {
 1480  306
                         log().debug("  Field '" + field.getName() + "' has a @Value annotation");
 1481  
                     }
 1482  306
                     ManagedPropertyConfig mpc = new ManagedPropertyConfig();
 1483  306
                     mpc.setName(field.getName());
 1484  306
                     mpc.setType(field.getType().getName()); // FIXME - primitives, arrays, etc.
 1485  306
                     mpc.setValue(value.value());
 1486  306
                     mbc.addProperty(mpc);
 1487  306
                     continue;
 1488  
                 }
 1489  
             }
 1490  54
             config.addManagedBean(mbc);
 1491  
         }
 1492  
 
 1493  144
     }
 1494  
 
 1495  
 
 1496  
     /**
 1497  
      * <p>Return a list of the JAR archives defined under the
 1498  
      * <code>/WEB-INF/lib</code> directory of this web application
 1499  
      * that contain a <code>META-INF/faces-config.xml</code> resource
 1500  
      * (even if that resource is empty).  If there are no such JAR archives,
 1501  
      * a zero-length list will be returned.</p>
 1502  
      *
 1503  
      * @param servletContext <code>ServletContext</code> instance for
 1504  
      *  this application
 1505  
      *
 1506  
      * @exception IOException if an input/output error occurs
 1507  
      */
 1508  
     private List<JarFile> webArchives(ServletContext servletContext)
 1509  
         throws IOException {
 1510  
 
 1511  18
         List<JarFile> list = new ArrayList<JarFile>();
 1512  18
         Set<Object> paths = servletContext.getResourcePaths(WEB_LIB_PREFIX);
 1513  18
         for (Object pathObject : paths) {
 1514  18
             String path = (String) pathObject;
 1515  18
             if (!path.endsWith(".jar")) {
 1516  
                 continue;
 1517  
             }
 1518  18
             URL url = servletContext.getResource(path);
 1519  18
             String jarURLString = "jar:" + url.toString() + "!/";
 1520  18
             url = new URL(jarURLString);
 1521  18
             JarFile jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
 1522  
             // Skip this JAR file if it does not have a META-INF/faces-config.xml
 1523  
             // resource (even if that resource is empty)
 1524  18
             JarEntry signal = jarFile.getJarEntry(FACES_CONFIG_IMPLICIT);
 1525  18
             if (signal == null) {
 1526  
                 if (log().isTraceEnabled()) {
 1527  
                     log().trace("Skip JAR file " + path + " because it has no META-INF/faces-config.xml resource");
 1528  
                 }
 1529  
                 continue;
 1530  
             }
 1531  18
             list.add(jarFile);
 1532  18
         }
 1533  18
         return list;
 1534  
 
 1535  
     }
 1536  
 
 1537  
 
 1538  
     /**
 1539  
      * <p>Return a list of the classes defined under the
 1540  
      * <code>/WEB-INF/classes</code> directory of this web
 1541  
      * application.  If there are no such classes, a zero-length list
 1542  
      * will be returned.</p>
 1543  
      *
 1544  
      * @param servletContext <code>ServletContext</code> instance for
 1545  
      *  this application
 1546  
      *
 1547  
      * @exception ClassNotFoundException if a located class cannot be loaded
 1548  
      */
 1549  
     private List<Class> webClasses(ServletContext servletContext)
 1550  
         throws ClassNotFoundException {
 1551  
 
 1552  18
         List<Class> list = new ArrayList<Class>();
 1553  18
         webClasses(servletContext, WEB_CLASSES_PREFIX, list);
 1554  18
         return list;
 1555  
 
 1556  
     }
 1557  
 
 1558  
 
 1559  
     /**
 1560  
      * <p>Add classes found in the specified directory to the specified
 1561  
      * list, recursively calling this method when a directory is encountered.</p>
 1562  
      *
 1563  
      * @param servletContext <code>ServletContext</code> instance for
 1564  
      *  this application
 1565  
      * @param prefix Prefix specifying the "directory path" to be searched
 1566  
      * @param list List to be appended to
 1567  
      *
 1568  
      * @exception ClassNotFoundException if a located class cannot be loaded
 1569  
      */
 1570  
     private void webClasses(ServletContext servletContext, String prefix, List<Class> list)
 1571  
         throws ClassNotFoundException {
 1572  
 
 1573  126
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
 1574  126
         if (loader == null) {
 1575  
             loader = this.getClass().getClassLoader();
 1576  
         }
 1577  126
         Set<Object> paths = servletContext.getResourcePaths(prefix);
 1578  126
         if (log().isTraceEnabled()) {
 1579  126
             log().trace("webClasses(" + prefix + ") - Received " + paths.size() + " paths to check");
 1580  
         }
 1581  126
         String path = null;
 1582  126
         for (Object pathObject : paths) {
 1583  234
             path = (String) pathObject;
 1584  234
             if (path.endsWith("/")) {
 1585  108
                 webClasses(servletContext, path, list);
 1586  108
             } else if (path.endsWith(".class")) {
 1587  126
                 path = path.substring(WEB_CLASSES_PREFIX.length()); // Strip prefix
 1588  126
                 path = path.substring(0, path.length() - 6);        // Strip suffix
 1589  126
                 path = path.replace('/', '.'); // Convert to FQCN
 1590  126
                 Class clazz = null;
 1591  
                 try {
 1592  126
                     clazz = loader.loadClass(path);
 1593  
                 } catch (NoClassDefFoundError e) {
 1594  
                     ; // Skip this class - we cannot analyze classes we cannot load
 1595  
                 } catch (Exception e) {
 1596  
                     ; // Skip this class - we cannot analyze classes we cannot load
 1597  126
                 }
 1598  126
                 if (clazz != null) {
 1599  126
                     list.add(clazz);
 1600  
                 }
 1601  
             }
 1602  234
         }
 1603  
 
 1604  126
     }
 1605  
 
 1606  
 
 1607  
 
 1608  
 }