dev@javaserverfaces.java.net

[REVIEW] Fleshed out implementation of FacesAnnotationHandler for spec 287

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Tue, 07 Oct 2008 13:21:23 -0700

This builds off Ed's initial revision.





SECTION: Modified Files
----------------------------
M jsf-api/src/javax/faces/application/Application.java
 - consistent usage of defaultApplicationImpl

M jsf-api/src/javax/faces/application/FacesAnnotationHandler.java
M jsf-api/src/javax/faces/application/FacesAnnotationHandlerWrapper.java
 - changed signature to accept FacesContext instead of ExternalContext.
   Seems more consistent with the rest of the API.

M jsf-ri/src/com/sun/faces/jsf-ri-config.xml
 - add reference to default FacesAnnotationProcessor implementation

M jsf-ri/src/com/sun/faces/application/ApplicationImpl.java
 - make the annotationHandler instance variable volatile and
   synchronize setFacesAnnotationHandler

M jsf-ri/src/com/sun/faces/config/processor/ApplicationConfigProcessor.java
 - add configuration/decoration logic for FacesAnntationHandler
 - remove execution of FacesAnnotationHandler

M jsf-ri/src/com/sun/faces/config/ConfigManager.java
 - After all of the ConfigProcessors have been invoked, store
   the Set of annotated classes in the ServletContext and then invoke
   FacesAnnotationHandler.processAnnotatedClasses().
   The default FacesAnnotationHandler implementation will pull the
   Set from the ServletContext and hand them off to the AnnotationManager.
   This allows us to keep the annotation multi-threaded without creating
   an extra Executor.


A jsf-ri/src/com/sun/faces/application/annotation/AnnotationHandler.java
 - default implementation of FacesAnnotationHandler.
   * There isn't a lot of logic here. Pull the collection from the ServletContext
     as pushed by ConfigManager and store it as a local variable.
   * processAnnotatedClass() calls through to AnnotationManager


SECTION: Diffs
----------------------------
Index: jsf-api/src/javax/faces/application/Application.java
===================================================================
--- jsf-api/src/javax/faces/application/Application.java (revision 5515)
+++ jsf-api/src/javax/faces/application/Application.java (working copy)
@@ -173,14 +173,12 @@
      */
     
     public FacesAnnotationHandler getFacesAnnotationHandler() {
- FacesAnnotationHandler result = null;
+
         if (defaultApplication != null) {
- result = defaultApplication.getFacesAnnotationHandler();
- } else {
- throw new UnsupportedOperationException();
+ return defaultApplication.getFacesAnnotationHandler();
         }
+ throw new UnsupportedOperationException();
         
- return result;
     }
 
     /**
Index: jsf-api/src/javax/faces/application/FacesAnnotationHandler.java
===================================================================
--- jsf-api/src/javax/faces/application/FacesAnnotationHandler.java (revision 5515)
+++ jsf-api/src/javax/faces/application/FacesAnnotationHandler.java (working copy)
@@ -41,7 +41,7 @@
 package javax.faces.application;
 
 import java.util.Set;
-import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
 
 /**
  * <p class="changed_added_2_0"><strong>FacesAnnotationHandler</strong>
@@ -52,17 +52,17 @@
  *
  * <div class="changed_added_2_0">
  *
- * <ul>
-
- <li><p>Call {_at_link #getClassNamesWithFacesAnnotations}, passing
- the startup time {_at_link ExternalContext}.</p></li>
-
- <li><p>Call {_at_link #processAnnotatedClasses}, passing the
- startup time <code>ExternalContext</code> and the result from
- the previous step.</p></li>
-
- </ul>
-
+ * <ul>
+ *
+ * <li><p>Call {_at_link #getClassNamesWithFacesAnnotations}, passing
+ * the startup time {_at_link FacesContext}.</p></li>
+ *
+ * <li><p>Call {_at_link #processAnnotatedClasses}, passing the
+ * startup time <code>FacesContext</code> and the result from
+ * the previous step.</p></li>
+ *
+ * </ul>
+ *
  * <p>If the <code>&lt;faces-config&gt;</code> element of the
  * application configuration resource file located at
  * <code>WEB-INF/faces-config.xml</code> contains a
@@ -85,10 +85,10 @@
  * <code>&lt;navigation-handler /&gt;</code> are processed
  * <em>after</em> the "startup time annotation processing algorithm" is
  * executed.</p>
-
+ *
  * <p>The runtime must employ the decorator pattern as for every other
  * pluggable artifact in JSF.</p>
-
+ *
  * </div>
  *
  * @since 2.0
@@ -100,21 +100,20 @@
      * string is a fully qualified java class name that must be
      * inspected for the presence of startup time annotations.</p>
      *
- * @param extContext the startup time <code>ExternalContext</code>
+ * @param context the startup time <code>FacesContext</code>
      * for this application.
      *
      * @since 2.0
      */
+ public abstract Set<String> getClassNamesWithFacesAnnotations(FacesContext context);
     
- public abstract Set<String> getClassNamesWithFacesAnnotations(ExternalContext extContext);
-
     /**
      * <p class="changed_added_2_0">Given the set of strings obtained
      * from {_at_link #getClassNamesWithFacesAnnotations}, process each
      * class to look for and handle startup time annotations as
      * described in the javadoc for each annotation.</p>
      *
- * @param extContext the startup time <code>ExternalContext</code>
+ * @param context the startup time <code>FacesContext</code>
      * for this application.
 
      * @param annotatedClassnames the <code>Set&lt;String&gt;</code>
@@ -122,9 +121,8 @@
      *
      * @since 2.0
      */
+ public abstract void processAnnotatedClasses(FacesContext context,
+ Set<String> annotatedClassnames);
 
- public abstract void processAnnotatedClasses(ExternalContext extContext,
- Set<String> annotatedClassnames);
 
-
 }
Index: jsf-api/src/javax/faces/application/FacesAnnotationHandlerWrapper.java
===================================================================
--- jsf-api/src/javax/faces/application/FacesAnnotationHandlerWrapper.java (revision 5515)
+++ jsf-api/src/javax/faces/application/FacesAnnotationHandlerWrapper.java (working copy)
@@ -38,7 +38,7 @@
 
 import java.util.Set;
 import javax.faces.FacesWrapper;
-import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
 
 /**
  * <p class="changed_added_2_0">Provides a simple implementation of
@@ -57,23 +57,29 @@
  *
  * @since 2.0
  */
-public abstract class FacesAnnotationHandlerWrapper extends FacesAnnotationHandler implements FacesWrapper<FacesAnnotationHandler> {
+public abstract class FacesAnnotationHandlerWrapper
+ extends FacesAnnotationHandler
+ implements FacesWrapper<FacesAnnotationHandler> {
 
     /**
      * @return the instance that we are wrapping.
- */
+ */
     public abstract FacesAnnotationHandler getWrapped();
 
+
+ // ------------------------------------- Methods from FacesAnnotationHandler
+
     /**
      * <p class="changed_added_2_0">The default behavior of this method
      * is to call {_at_link
      * FacesAnnotationHandler#getClassNamesWithFacesAnnotations}
      * on the wrapped {_at_link FacesAnnotationHandler} object.</p>
      */
+ @Override
+ public Set<String> getClassNamesWithFacesAnnotations(FacesContext context) {
 
- @Override
- public Set<String> getClassNamesWithFacesAnnotations(ExternalContext extContext) {
- return getWrapped().getClassNamesWithFacesAnnotations(extContext);
+ return getWrapped().getClassNamesWithFacesAnnotations(context);
+
     }
 
    /**
@@ -82,12 +88,15 @@
      * on the wrapped {_at_link FacesAnnotationHandler} object.</p>
      */
     @Override
- public void processAnnotatedClasses(ExternalContext extContext, Set<String> annotatedClasses) {
- getWrapped().processAnnotatedClasses(extContext, annotatedClasses);
+ public void processAnnotatedClasses(FacesContext context,
+ Set<String> annotatedClasses) {
+
+ getWrapped().processAnnotatedClasses(context, annotatedClasses);
+
     }
 
 
- // -------------------------------------------- Methods from FacesAnnotationHandler
 
-
+
+
 }

Index: jsf-ri/src/com/sun/faces/jsf-ri-config.xml
===================================================================
--- jsf-ri/src/com/sun/faces/jsf-ri-config.xml (revision 5515)
+++ jsf-ri/src/com/sun/faces/jsf-ri-config.xml (working copy)
@@ -80,6 +80,9 @@
     <resource-handler>
       com.sun.faces.application.resource.ResourceHandlerImpl
     </resource-handler>
+ <annotation-handler>
+ com.sun.faces.application.annotation.AnnotationHandler
+ </annotation-handler>
     <system-event-listener>
         <system-event-listener-class>
             com.sun.faces.config.listeners.ProjectStagePhaseListenerInstallationListener
Index: jsf-ri/src/com/sun/faces/application/ApplicationImpl.java
===================================================================
--- jsf-ri/src/com/sun/faces/application/ApplicationImpl.java (revision 5515)
+++ jsf-ri/src/com/sun/faces/application/ApplicationImpl.java (working copy)
@@ -535,25 +535,29 @@
     public ActionListener getActionListener() {
         return actionListener;
     }
-
- /*
- * RELEASE_PENDING(rlubke) note that my implementation doesn't rely on
- * jsf-ri-config.xml to install the default implementation. It could, and
- * maybe it should. It's up to you.
+
+
+ /**
+ * @see javax.faces.application.Application#getFacesAnnotationHandler()
+ * @return
      */
     @Override
     public FacesAnnotationHandler getFacesAnnotationHandler() {
- if (null == annotationHandler) {
- annotationHandler = ConfigManager.createAnnotationHandler();
- }
+
         return annotationHandler;
+
     }
 
+
+ /**
+ * @see javax.faces.application.Application#setFacesAnnotationHandler(javax.faces.application.FacesAnnotationHandler)
+ */
     @Override
- public void setFacesAnnotationHandler(FacesAnnotationHandler newHandler) {
+ public synchronized void setFacesAnnotationHandler(FacesAnnotationHandler newHandler) {
+
         annotationHandler = newHandler;
+
     }
-
 
 
     /**
Index: jsf-ri/src/com/sun/faces/application/annotation/AnnotationHandler.java
===================================================================
--- jsf-ri/src/com/sun/faces/application/annotation/AnnotationHandler.java (revision 0)
+++ jsf-ri/src/com/sun/faces/application/annotation/AnnotationHandler.java (revision 0)
@@ -0,0 +1,115 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can obtain
+ * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
+ * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
+ * Sun designates this particular file as subject to the "Classpath" exception
+ * as provided by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the License
+ * Header, with the fields enclosed by brackets [] replaced by your own
+ * identifying information: "Portions Copyrighted [year]
+ * [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package com.sun.faces.application.annotation;
+
+import java.util.Set;
+import java.util.Collections;
+import java.util.Map;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import javax.faces.application.FacesAnnotationHandler;
+import javax.faces.context.FacesContext;
+
+import com.sun.faces.application.ApplicationAssociate;
+import com.sun.faces.util.FacesLogger;
+
+/**
+ *
+ */
+public class AnnotationHandler extends FacesAnnotationHandler {
+
+ private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
+
+ /**
+ * Key under which a Set of annotated classes names will be stored within
+ * the <code>ServletContext</code>.
+ */
+ public static final String ANNOTATED_CLASSNAMES_KEY =
+ AnnotationHandler.class.getName() + "_ANNOTATED_CLASSED";
+
+ /**
+ * <p>
+ * The annotated classes.
+ * </p>
+ */
+ private Set<String> annotatedClasses;
+
+
+ // ------------------------------------- Methods from FacesAnnotationHandler
+
+
+ public Set<String> getClassNamesWithFacesAnnotations(FacesContext context) {
+
+ if (annotatedClasses == null) {
+ annotatedClasses = getAnnotatedClasses(context);
+ }
+ return annotatedClasses;
+
+ }
+
+ public void processAnnotatedClasses(FacesContext context,
+ Set<String> annotatedClassnames) {
+
+ ApplicationAssociate associate =
+ ApplicationAssociate.getInstance(context.getExternalContext());
+ if (associate != null) {
+ AnnotationManager manager = associate.getAnnotationManager();
+ assert (manager != null);
+ manager.applyConfigAnntations(context, annotatedClassnames);
+ } else {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine("No ApplicationAssociate available.");
+ }
+ }
+
+ }
+
+
+ // --------------------------------------------------------- Private Methods
+
+
+ private Set<String> getAnnotatedClasses(FacesContext ctx) {
+
+ Map<String,Object> appMap = ctx.getExternalContext().getApplicationMap();
+ //noinspection unchecked
+ Set<String> classes = (Set<String>) appMap.remove(ANNOTATED_CLASSNAMES_KEY);
+ return ((classes != null) ? classes : Collections.<String>emptySet());
+
+ }
+
+}

Property changes on: jsf-ri/src/com/sun/faces/application/annotation/AnnotationHandler.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Index: jsf-ri/src/com/sun/faces/config/processor/ApplicationConfigProcessor.java
===================================================================
--- jsf-ri/src/com/sun/faces/config/processor/ApplicationConfigProcessor.java (revision 5515)
+++ jsf-ri/src/com/sun/faces/config/processor/ApplicationConfigProcessor.java (working copy)
@@ -57,6 +57,7 @@
 import javax.faces.application.ResourceHandler;
 import javax.faces.application.StateManager;
 import javax.faces.application.ViewHandler;
+import javax.faces.application.FacesAnnotationHandler;
 import javax.faces.context.FacesContext;
 import javax.faces.el.PropertyResolver;
 import javax.faces.el.VariableResolver;
@@ -73,9 +74,6 @@
 import com.sun.faces.config.ConfigurationException;
 import com.sun.faces.config.WebConfiguration;
 import com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter;
-import javax.faces.application.FacesAnnotationHandler;
-import javax.faces.context.ExternalContext;
-import javax.faces.context.ExternalContext;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -140,6 +138,12 @@
          = "resource-handler";
 
     /**
+ * <code>/faces-config/application/annotation-handler</code>
+ */
+ private static final String ANNOTATION_HANDLER =
+ "annotation-handler";
+
+ /**
      * <code>/faces-config/application/el-resolver</code>
      */
     private static final String EL_RESOLVER
@@ -291,6 +295,8 @@
                                 setResourceHandler(app, n);
                             } else if (SYSTEM_EVENT_LISTENER.equals(n.getLocalName())) {
                                 addSystemEventListener(app, n);
+ } else if (ANNOTATION_HANDLER.equals(n.getLocalName())) {
+ setAnnotationHandler(app, n);
                             }
                         }
                     }
@@ -300,22 +306,11 @@
 
         processViewHandlers(app, viewHandlers);
         
- processAnnotations(app);
-
         invokeNext(documents);
 
     }
 
- private void processAnnotations(Application app) {
- FacesContext facesContext = FacesContext.getCurrentInstance();
- FacesAnnotationHandler annotationHandler = app.getFacesAnnotationHandler();
- ExternalContext extContext = facesContext.getExternalContext();
 
- Set<String> fqcns = annotationHandler.getClassNamesWithFacesAnnotations(extContext);
- annotationHandler.processAnnotatedClasses(extContext, fqcns);
- }
-
-
     // --------------------------------------------------------- Private Methods
 
 
@@ -764,4 +759,27 @@
         }
     }
 
+
+ private void setAnnotationHandler(Application application, Node annotationHandler) {
+
+ if (annotationHandler != null) {
+ String handler = getNodeText(annotationHandler);
+ if (handler != null) {
+ Object instance = createInstance(handler,
+ FacesAnnotationHandler.class,
+ application.getResourceHandler(),
+ annotationHandler);
+ if (instance != null) {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ MessageFormat.format(
+ "Calling Application.setAnnotationHandler({0})",
+ handler));
+ }
+ application.setFacesAnnotationHandler((FacesAnnotationHandler) instance);
+ }
+ }
+ }
+ }
+
 }
Index: jsf-ri/src/com/sun/faces/config/ConfigManager.java
===================================================================
--- jsf-ri/src/com/sun/faces/config/ConfigManager.java (revision 5515)
+++ jsf-ri/src/com/sun/faces/config/ConfigManager.java (working copy)
@@ -63,7 +63,7 @@
 import com.sun.faces.util.Timer;
 import com.sun.faces.application.ApplicationAssociate;
 import com.sun.faces.application.annotation.AnnotationManager;
-import javax.faces.context.ExternalContext;
+import com.sun.faces.application.annotation.AnnotationHandler;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
 
@@ -71,6 +71,7 @@
 import javax.faces.FactoryFinder;
 import javax.faces.event.ApplicationPostConstructEvent;
 import javax.faces.application.Application;
+import javax.faces.application.FacesAnnotationHandler;
 import javax.faces.context.FacesContext;
 import javax.servlet.ServletContext;
 import javax.xml.parsers.DocumentBuilder;
@@ -101,7 +102,6 @@
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import javax.faces.application.FacesAnnotationHandler;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Node;
 
@@ -238,7 +238,7 @@
 
         // initialize the config processor for facelet-taglib documents
         FACELET_TAGLIB_CONFIG_PROCESSOR_CHAIN = new FaceletTaglibConfigProcessor();
-
+
     }
 
 
@@ -253,43 +253,8 @@
         return CONFIG_MANAGER;
 
     }
-
- public static FacesAnnotationHandler createAnnotationHandler() {
- FacesAnnotationHandler result = null;
-
- result = new javax.faces.application.FacesAnnotationHandler() {
 
- @Override
- public Set<String> getClassNamesWithFacesAnnotations(ExternalContext extContext) {
- Set<String> result = null;
- ExecutorService executor = createExecutorService();
 
- // execut the Task responsible for finding annotation classes
- Future<Set<String>> annotationScan =
- executor.submit(new AnnotationScanTask((ServletContext) extContext.getContext()));
- try {
-
- result = annotationScan.get();
- } catch (InterruptedException ex) {
- LOGGER.log(Level.SEVERE, null, ex);
- } catch (ExecutionException ex) {
- LOGGER.log(Level.SEVERE, null, ex);
- }
- executor.shutdown();
- return result;
- }
-
- @Override
- public void processAnnotatedClasses(ExternalContext extContext,
- Set<String> fqcns) {
- ConfigManager.processAnnotatedClasses((ServletContext)extContext.getContext(), fqcns);
- }
- };
-
- return result;
- }
-
-
     /**
      * <p>
      * This method bootstraps JSF based on the parsed configuration resources.
@@ -307,12 +272,16 @@
                 boolean validating = webConfig.isOptionEnabled(ValidateFacesConfigFiles);
                 ExecutorService executor = createExecutorService();
 
+ // execut the Task responsible for finding annotation classes
+ Future<Set<String>> annotationScan =
+ executor.submit(new AnnotationScanTask(sc));
+
                 Document[] facesDocuments =
                       getConfigDocuments(sc,
                                          FACES_CONFIG_RESOURCE_PROVIDERS,
                                          executor,
                                          validating);
-
+
                 boolean isFaceletsDisabled =
                       isFaceletsDisabled(webConfig, facesDocuments[ facesDocuments.length - 1]);
 
@@ -329,8 +298,9 @@
                                              validating));
                 }
 
- executor.shutdown();
+ processAnnotatedClasses(sc, annotationScan.get());
 
+ executor.shutdown();
                 publishPostConfigEvent();
             } catch (Exception e) {
                 // clear out any configured factories
@@ -415,22 +385,22 @@
 
 
     /**
- * Call through to {_at_link AnnotationManager#applyConfigAnntations(javax.faces.context.FacesContext, java.util.Collection)}
- * if any annotated classes are found.
- * @param sc the <code>ServletContext</code> for this application
- * @param annotatedClasses <code>Set</code> of classes annotated with Faces
+ * Call through to {_at_link FacesAnnotationHandler#processAnnotatedClasses(javax.faces.context.FacesContext, java.util.Set)}
+ * to configure artifacts associated with the discovered annotations.
      * configration annotations
      */
- private static void processAnnotatedClasses(ServletContext sc, Set<String> annotatedClasses) {
+ private void processAnnotatedClasses(ServletContext sc,
+ Set<String> annotatedClasses) {
 
- if (!annotatedClasses.isEmpty()) {
- ApplicationAssociate associate = ApplicationAssociate.getInstance(sc);
- if (associate != null) {
- AnnotationManager manager = associate.getAnnotationManager();
- manager.applyConfigAnntations(FacesContext.getCurrentInstance(),
- annotatedClasses);
- }
- }
+ // push annotatedClasses to the provided ServletContext before invoking
+ // the FacesAnnotationHandler so that the default FacesAnnotationHandler
+ // has access to the results.
+ sc.setAttribute(AnnotationHandler.ANNOTATED_CLASSNAMES_KEY,
+ annotatedClasses);
+ FacesContext ctx = FacesContext.getCurrentInstance();
+ Application app = ctx.getApplication();
+ FacesAnnotationHandler handler = app.getFacesAnnotationHandler();
+ handler.processAnnotatedClasses(ctx, handler.getClassNamesWithFacesAnnotations(ctx));
 
     }
 


SECTION: New Files
----------------------------
SEE ATTACHMENTS