You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mc...@apache.org on 2009/06/17 16:46:57 UTC

svn commit: r785633 - in /myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces: config/ config/annotation/ config/impl/digester/ config/impl/digester/elements/ view/facelets/tag/jsf/core/

Author: mconcini
Date: Wed Jun 17 14:46:57 2009
New Revision: 785633

URL: http://svn.apache.org/viewvc?rev=785633&view=rev
Log:
MYFACES-2253 - finish system event support

Added:
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/NamedEventManager.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/SystemEventListener.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java
Modified:
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/annotation/AnnotationConfigurator.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/Application.java
    myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigDispenser.java Wed Jun 17 14:46:57 2009
@@ -27,6 +27,7 @@
 import org.apache.myfaces.config.element.Renderer;
 import org.apache.myfaces.config.impl.digester.elements.Converter;
 import org.apache.myfaces.config.impl.digester.elements.ResourceBundle;
+import org.apache.myfaces.config.impl.digester.elements.SystemEventListener;
 
 /**
  * Subsumes several unmarshalled faces config objects and presents a simple interface
@@ -241,4 +242,9 @@
      * @return Iterator over {@link ELResolver} implementation class names
      */
     public Collection<String> getElResolvers();
+    
+    /**
+     * @return Iterator over (@link SystemEventListener) implementation class names 
+     */
+    public Collection<SystemEventListener> getSystemEventListeners();
 }

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java Wed Jun 17 14:46:57 2009
@@ -60,6 +60,7 @@
 import javax.faces.el.VariableResolver;
 import javax.faces.event.ActionListener;
 import javax.faces.event.PhaseListener;
+import javax.faces.event.SystemEvent;
 import javax.faces.lifecycle.Lifecycle;
 import javax.faces.lifecycle.LifecycleFactory;
 import javax.faces.render.RenderKit;
@@ -81,6 +82,7 @@
 import org.apache.myfaces.config.impl.digester.elements.FacesConfigNameSlot;
 import org.apache.myfaces.config.impl.digester.elements.OrderSlot;
 import org.apache.myfaces.config.impl.digester.elements.ResourceBundle;
+import org.apache.myfaces.config.impl.digester.elements.SystemEventListener;
 import org.apache.myfaces.context.FacesContextFactoryImpl;
 import org.apache.myfaces.context.PartialViewContextFactoryImpl;
 import org.apache.myfaces.el.DefaultPropertyResolver;
@@ -1568,6 +1570,14 @@
                                                         dispenser.getViewHandlerIterator(),
                                                         application.getViewHandler()));
 
+        for (SystemEventListener systemEventListener : dispenser.getSystemEventListeners())
+        {
+            
+            application.subscribeToEvent(
+                    (Class<? extends SystemEvent>)ClassUtils.newInstance(systemEventListener.getSystemEventClass(),SystemEvent.class), 
+                    (Class<?>)ClassUtils.newInstance(systemEventListener.getSourceClass()), 
+                    (javax.faces.event.SystemEventListener)ClassUtils.newInstance(systemEventListener.getSystemEventClass(),javax.faces.event.SystemEventListener.class));
+        }
         
         for (String componentType : dispenser.getComponentTypes())
         {

Added: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/NamedEventManager.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/NamedEventManager.java?rev=785633&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/NamedEventManager.java (added)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/NamedEventManager.java Wed Jun 17 14:46:57 2009
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.config;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import javax.faces.event.ComponentSystemEvent;
+import javax.faces.event.PostAddToViewEvent;
+import javax.faces.event.PostValidateEvent;
+import javax.faces.event.PreRenderComponentEvent;
+import javax.faces.event.PreValidateEvent;
+
+/**
+ * The NamedEventManager class is used to keep map a short name to ComponentSystemEvent classes
+ * annotated with @NamedEvent.
+ */
+
+public class NamedEventManager {
+	private static final NamedEventManager instance = new NamedEventManager();
+	
+	private HashMap<String, Collection<Class<? extends ComponentSystemEvent>>> events;
+	
+	private NamedEventManager ()
+	{
+		events = new HashMap<String, Collection<Class<? extends ComponentSystemEvent>>>();
+		
+		// Special spec-defined values.
+		
+		addNamedEvent ("postAddToView", PostAddToViewEvent.class);
+		addNamedEvent ("preRenderComponent", PreRenderComponentEvent.class);
+		addNamedEvent ("preValidate", PreValidateEvent.class);
+		addNamedEvent ("postValidate", PostValidateEvent.class);
+	}
+	
+	public static NamedEventManager getInstance ()
+	{
+		return instance;
+	}
+	
+	/**
+	 * Registers a named event.
+	 * 
+	 * @param shortName a String containing the short name for the event, from the @NamedEvent.shortName()
+	 *        attribute.
+	 * @param cls the event class to register.
+	 */
+	
+	public void addNamedEvent (String shortName, Class<? extends ComponentSystemEvent> cls)
+	{
+		String key = shortName;
+		Collection<Class<? extends ComponentSystemEvent>> eventList;
+		
+		// Per the spec, if the short name is missing, generate one.
+		
+		if (shortName == null) {
+			key = getFixedName (cls);
+		}
+		
+		eventList = events.get (key);
+		
+		if (eventList == null) {
+			// First event registered to this short name.
+			
+			eventList = new LinkedList<Class<? extends ComponentSystemEvent>>();
+			
+			events.put (key, eventList);
+		}
+		
+		eventList.add (cls);
+	}
+	
+	/**
+	 * Retrieves a collection of system event classes based on their short name.
+	 * 
+	 * @param shortName the short name to look up.
+	 * @return a Collection of Class objects containing the system event classes registered to
+	 *         the given short name.
+	 */
+	
+	public Collection<Class<? extends ComponentSystemEvent>> getNamedEvent (String shortName)
+	{
+		return events.get (shortName);
+	}
+	
+	/**
+	 * Retrieves the short name for an event class, according to spec rules.
+	 * 
+	 * @param cls the class to find the short name for.
+	 * @return a String containing the short name for the given class.
+	 */
+	
+	private String getFixedName (Class<? extends ComponentSystemEvent> cls)
+	{
+		StringBuilder result = new StringBuilder();
+		String className;
+		
+		// Get the unqualified class name.
+		
+		className = cls.getSimpleName();
+		
+		// Strip the trailing "event" off the class name if present.
+		
+		if (className.toLowerCase().endsWith ("event")) {
+			className = className.substring (0, result.length() - 5);
+		}
+		
+		// Prepend the package name.
+		
+		if (cls.getPackage() != null) {
+			result.append (cls.getPackage().getName());
+			result.append ('.');
+		}
+		
+		result.append (className);
+		
+		return result.toString();
+	}
+}

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/annotation/AnnotationConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/annotation/AnnotationConfigurator.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/annotation/AnnotationConfigurator.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/annotation/AnnotationConfigurator.java Wed Jun 17 14:46:57 2009
@@ -48,6 +48,8 @@
 import javax.faces.component.FacesComponent;
 import javax.faces.context.ExternalContext;
 import javax.faces.convert.FacesConverter;
+import javax.faces.event.ComponentSystemEvent;
+import javax.faces.event.NamedEvent;
 import javax.faces.render.FacesRenderer;
 import javax.faces.render.RenderKit;
 import javax.faces.render.RenderKitFactory;
@@ -57,6 +59,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.myfaces.config.FacesConfigDispenser;
+import org.apache.myfaces.config.NamedEventManager;
 import org.apache.myfaces.config.RuntimeConfig;
 import org.apache.myfaces.config.impl.digester.elements.FacesConfig;
 import org.apache.myfaces.shared_impl.util.ClassUtils;
@@ -851,6 +854,30 @@
             runtimeConfig.addManagedBean(mbc.getManagedBeanName(), mbc);
         }
         
+        NamedEvent namedEvent = (NamedEvent) clazz.getAnnotation (NamedEvent.class);
+        
+        if (namedEvent != null) {
+        	// Can only apply @NamedEvent to ComponentSystemEvent subclasses.
+        	
+        	if (!ComponentSystemEvent.class.isAssignableFrom (clazz)) {
+        		// Just log this.  We'll catch it later in the runtime.
+        		
+        		if (log.isWarnEnabled()) {
+        			log.warn (clazz.getName() + " is annotated with @javax.faces.event.NamedEvent, but does " +
+        				"not extend javax.faces.event.ComponentSystemEvent");
+        		}
+        		
+        		return;
+        	}
+        	
+        	// Have to register @NamedEvent annotations with the NamedEventManager class since
+        	// we need to get access to this info later and can't from the dispenser (it's not a
+        	// singleton).
+        	
+        	NamedEventManager.getInstance().addNamedEvent (namedEvent.shortName(),
+        		(Class<? extends ComponentSystemEvent>) clazz);
+        }
+        
         // TODO: All annotations scanned at startup must be configured here!
         //FacesBehaviorRenderer
     }

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigDispenserImpl.java Wed Jun 17 14:46:57 2009
@@ -39,6 +39,7 @@
 import org.apache.myfaces.config.impl.digester.elements.LocaleConfig;
 import org.apache.myfaces.config.impl.digester.elements.RenderKit;
 import org.apache.myfaces.config.impl.digester.elements.ResourceBundle;
+import org.apache.myfaces.config.impl.digester.elements.SystemEventListener;
 
 /**
  * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a>
@@ -86,6 +87,8 @@
     private List<NavigationRule> navigationRules = new ArrayList<NavigationRule>();
     private List<ResourceBundle> resourceBundles = new ArrayList<ResourceBundle>();
 
+    private List<SystemEventListener> systemEventListeners = new ArrayList<SystemEventListener>();
+    
     /**
      * Add another unmarshalled faces config object.
      * 
@@ -138,6 +141,7 @@
             variableResolver.addAll(application.getVariableResolver());
             resourceBundles.addAll(application.getResourceBundle());
             elResolvers.addAll(application.getElResolver());
+            systemEventListeners.addAll(application.getSystemEventListener());
         }
 
         for (Converter converter : config.getConverters())
@@ -553,4 +557,10 @@
         return elResolvers;
     }
 
+    @Override
+    public Collection<SystemEventListener> getSystemEventListeners()
+    {        
+        return systemEventListeners;
+    }
+
 }

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/DigesterFacesConfigUnmarshallerImpl.java Wed Jun 17 14:46:57 2009
@@ -100,6 +100,11 @@
         // 1.2 specific end
 
         // 2.0 specific start
+        digester.addObjectCreate("faces-config/application/system-event-listener", SystemEventListener.class);
+        digester.addSetNext("faces-config/application/system-event-listener", "addSystemEventListener");
+        digester.addCallMethod("faces-config/application/system-event-listener/system-event-listener-class", "setSystemEventListenerClass",0);
+        digester.addCallMethod("faces-config/application/system-event-listener/system-event-class", "setSystemEventClass",0);
+        digester.addCallMethod("faces-config/application/system-event-listener/source-class", "setSourceClass",0);
         digester.addCallMethod("faces-config/application/resource-handler", "addResourceHandler", 0);
         digester.addCallMethod("faces-config/factory/exception-handler-factory", "addExceptionHandlerFactory", 0);
         digester.addCallMethod("faces-config/factory/external-context-factory", "addExternalContextFactory", 0);

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/Application.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/Application.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/Application.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/Application.java Wed Jun 17 14:46:57 2009
@@ -39,7 +39,8 @@
     private final List<LocaleConfig> localeConfig = new ArrayList<LocaleConfig>();
     private final List<String> elResolver = new ArrayList<String>();
     private final List<ResourceBundle> resourceBundle = new ArrayList<ResourceBundle>();
-
+    private final List<SystemEventListener> systemEventListener = new ArrayList<SystemEventListener>();
+    
     public void addActionListener(String listener)
     {
         actionListener.add(listener);
@@ -100,6 +101,11 @@
         resourceBundle.add(bundle);
     }
 
+    public void addSystemEventListener(SystemEventListener listener)
+    {
+        systemEventListener.add(listener);
+    }
+    
     public List<String> getActionListener()
     {
         return actionListener;
@@ -159,4 +165,9 @@
     {
         return resourceBundle;
     }
+
+    public List<SystemEventListener> getSystemEventListener()
+    {
+        return systemEventListener;
+    }
 }

Added: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/SystemEventListener.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/SystemEventListener.java?rev=785633&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/SystemEventListener.java (added)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/SystemEventListener.java Wed Jun 17 14:46:57 2009
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+package org.apache.myfaces.config.impl.digester.elements;
+
+public class SystemEventListener
+{
+    private String systemEventListenerClass;
+    private String systemEventClass;
+    private String sourceClass;
+    
+    public void setSystemEventListenerClass(String listener)
+    {
+        systemEventListenerClass = listener;
+    }
+
+    public void setSystemEventClass(String event)
+    {
+        systemEventClass = event;
+    }
+    
+    public void setSourceClass(String source)
+    {
+        sourceClass = source;
+    }
+    
+    public String getSystemEventListenerClass()
+    {
+        return systemEventListenerClass;
+    }
+
+    public String getSystemEventClass()
+    {
+        return systemEventClass;
+    }
+
+    public String getSourceClass()
+    {
+        return sourceClass;
+    }
+}

Modified: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java?rev=785633&r1=785632&r2=785633&view=diff
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java (original)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java Wed Jun 17 14:46:57 2009
@@ -57,7 +57,9 @@
         this.addConverter("convertNumber", NumberConverter.CONVERTER_ID, ConvertNumberHandler.class);
 
         this.addConverter("converter", null, ConvertDelegateHandler.class);
-
+        
+        this.addTagHandler ("event", EventHandler.class);
+        
         this.addTagHandler("facet", FacetHandler.class);
 
         this.addTagHandler("loadBundle", LoadBundleHandler.class);

Added: myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java?rev=785633&view=auto
==============================================================================
--- myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java (added)
+++ myfaces/core/branches/2_0_0/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/EventHandler.java Wed Jun 17 14:46:57 2009
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.view.facelets.tag.jsf.core;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.component.UIComponent;
+import javax.faces.event.ComponentSystemEvent;
+import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.view.facelets.FaceletContext;
+import javax.faces.view.facelets.FaceletException;
+import javax.faces.view.facelets.TagAttribute;
+import javax.faces.view.facelets.TagAttributeException;
+import javax.faces.view.facelets.TagConfig;
+import javax.faces.view.facelets.TagException;
+import javax.faces.view.facelets.TagHandler;
+
+import org.apache.myfaces.config.NamedEventManager;
+import org.apache.myfaces.view.facelets.util.ReflectionUtil;
+
+/**
+ * Registers a listener for a given system event class on the UIComponent associated with this tag.
+ */
+
+public final class EventHandler extends TagHandler {
+	private TagAttribute listener;
+	private TagAttribute name;
+	private TagAttribute type;
+	
+	public EventHandler (TagConfig tagConfig)
+	{
+		super (tagConfig);
+		
+		listener = getRequiredAttribute ("listener");
+		name = getAttribute ("name");
+		type = getAttribute ("type");
+		
+		// TODO: is this right?  The spec isn't entirely clear, but it seems to me that one or the other
+		// attribute must be defined, despite the fact that the docs say "name" is required.
+		
+		if ((name == null) && (type == null)) {
+			throw new TagException (this.tag, "One of the 'name' or 'type' attributes must be defined");
+		}
+		
+		else if ((name != null) && (type != null)) {
+			throw new TagException (this.tag, "Both the 'name' and 'type' attributes cannot be defined");
+		}
+	}
+	
+	@Override
+	public void apply (FaceletContext ctx, UIComponent parent) throws ELException, FacesException, FaceletException, IOException
+	{
+		Class<? extends ComponentSystemEvent> eventClass = getEventClass (ctx);
+		MethodExpression methodExp = listener.getMethodExpression(ctx, void.class, new Class<?>[] {
+			ComponentSystemEvent.class });
+		
+		// Simply register the event on the component.
+		
+		parent.subscribeToEvent (eventClass, new Listener (ctx.getFacesContext().getELContext(), methodExp));
+	}
+	
+	/**
+	 * Gets the event class defined by the tag (either in the "name" or "type" attribute).
+	 * 
+	 * @param context the Facelet context
+	 * @return a Class containing the event class defined by the tag.
+	 */
+	
+	@SuppressWarnings("unchecked")
+	private Class<? extends ComponentSystemEvent> getEventClass (FaceletContext context)
+	{
+		Class<?> eventClass = null;
+		ValueExpression valueExp = ((name != null) ? name.getValueExpression (context, String.class) :
+			type.getValueExpression (context, String.class));
+		String value = (String) valueExp.getValue (context.getFacesContext().getELContext());
+		
+		if (name != null) {
+			Collection<Class<? extends ComponentSystemEvent>> events;
+			
+			// We can look up the event class by name in the NamedEventManager.
+			
+			events = NamedEventManager.getInstance().getNamedEvent (value);
+			
+			if (events.size() > 1) {
+				StringBuilder classNames = new StringBuilder ("[");
+				Iterator<Class<? extends ComponentSystemEvent>> eventIterator = events.iterator();
+				
+				// TODO: The spec is somewhat vague, but I think we're supposed to throw an exception
+				// here.  The @NamedEvent javadocs say that if a short name is registered to more than one
+				// event class that we must throw an exception listing the short name and the classes in
+				// the list _when the application makes reference to it_.  I believe processing this tag
+				// qualifies as the application "making reference" to the short name.  Why the exception
+				// isn't thrown when processing the @NamedEvent annotation, I don't know.  Perhaps follow
+				// up with the EG to see if this is correct.
+				
+				while (eventIterator.hasNext()) {
+					classNames.append (eventIterator.next().getName());
+					
+					if (eventIterator.hasNext()) {
+						classNames.append (", ");
+					}
+					
+					else {
+						classNames.append ("]");
+					}
+				}
+				
+				throw new FacesException ("The event name '" + value + "' is mapped to more than one " +
+					" event class: " + classNames.toString());
+			}
+			
+			else {
+				eventClass = events.iterator().next();
+			}
+		}
+		
+		else {
+			// Must have been defined via the "type" attribute, so instantiate the class.
+			
+			try {
+				eventClass = ReflectionUtil.forName (value);
+			}
+		
+			catch (Throwable e) {
+				throw new TagAttributeException ((name != null) ? name : type, "Couldn't create event class", e);
+			}
+		}
+		
+		if (!ComponentSystemEvent.class.isAssignableFrom (eventClass)) {
+			throw new TagAttributeException ((name != null) ? name : type, "Event class " + eventClass.getName() +
+				" is not of type javax.faces.event.ComponentSystemEvent");
+		}
+		
+		return (Class<? extends ComponentSystemEvent>) eventClass;
+	}
+	
+	private class Listener implements ComponentSystemEventListener {
+		private ELContext elContext;
+		private MethodExpression methodExp;
+		
+		private Listener (ELContext elContext, MethodExpression methodExp)
+		{
+			this.elContext = elContext;
+			this.methodExp = methodExp;
+		}
+		
+		@Override
+		public void processEvent (ComponentSystemEvent event)
+		{
+			this.methodExp.invoke(elContext, new Object[] { event });
+		}
+	}
+}