You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/07/07 20:28:12 UTC

svn commit: r961463 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/internal/transform/ main/java/org/apache/tapestry5/runtime/ main/java/org/apache/tapestry5/services/ ...

Author: hlship
Date: Wed Jul  7 18:28:11 2010
New Revision: 961463

URL: http://svn.apache.org/viewvc?rev=961463&view=rev
Log:
TAP5-1199: ClassTransformation should include an API specifically for adding a component event handler to a component class

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ActivationRequestParameterWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageActivationContextWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/runtime/ComponentEvent.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java Wed Jul  7 18:28:11 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
 //
 // Licensed 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
+// 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,
@@ -31,16 +31,21 @@ public class ComponentEventImpl extends 
     private final ComponentPageElementResources elementResources;
 
     /**
-     * @param eventType              non blank string used to identify the type of event that was triggered
-     * @param originatingComponentId the id of the component that triggered the event
-     * @param context                provides access to parameter values
-     * @param handler                invoked when a non-null return value is obtained from an event handler method
-     * @param elementResources       provides access to common resources and services
-     * @param logger                 used to log method invocations
+     * @param eventType
+     *            non blank string used to identify the type of event that was triggered
+     * @param originatingComponentId
+     *            the id of the component that triggered the event
+     * @param context
+     *            provides access to parameter values
+     * @param handler
+     *            invoked when a non-null return value is obtained from an event handler method
+     * @param elementResources
+     *            provides access to common resources and services
+     * @param logger
+     *            used to log method invocations
      */
     public ComponentEventImpl(String eventType, String originatingComponentId, EventContext context,
-                              ComponentEventCallback handler,
-                              ComponentPageElementResources elementResources, Logger logger)
+            ComponentEventCallback handler, ComponentPageElementResources elementResources, Logger logger)
     {
         super(handler, logger);
 
@@ -50,26 +55,27 @@ public class ComponentEventImpl extends 
         this.context = context;
     }
 
-
     @Override
     public String toString()
     {
-        return String.format("ComponentEvent[%s from %s]", eventType,
-                             originatingComponentId.length() == 0 ? "(self)" : originatingComponentId);
+        return String.format("ComponentEvent[%s from %s]", eventType, originatingComponentId.length() == 0 ? "(self)"
+                : originatingComponentId);
     }
 
     public boolean matches(String eventType, String componentId, int parameterCount)
     {
-        return this.eventType.equalsIgnoreCase(
-                eventType) && context.getCount() >= parameterCount && (originatingComponentId.equalsIgnoreCase(
-                componentId) || componentId.equals(""));
+        if (isAborted())
+            return false;
+
+        return this.eventType.equalsIgnoreCase(eventType) && context.getCount() >= parameterCount
+                && (originatingComponentId.equalsIgnoreCase(componentId) || componentId.equals(""));
     }
 
     @SuppressWarnings("unchecked")
     public Object coerceContext(int index, String desiredTypeName)
     {
-        if (index >= context.getCount()) throw new IllegalArgumentException(ServicesMessages
-                .contextIndexOutOfRange(getMethodDescription()));
+        if (index >= context.getCount())
+            throw new IllegalArgumentException(ServicesMessages.contextIndexOutOfRange(getMethodDescription()));
         try
         {
             Class desiredType = elementResources.toClass(desiredTypeName);
@@ -78,8 +84,8 @@ public class ComponentEventImpl extends 
         }
         catch (Exception ex)
         {
-            throw new IllegalArgumentException(
-                    ServicesMessages.exceptionInMethodParameter(getMethodDescription(), index, ex), ex);
+            throw new IllegalArgumentException(ServicesMessages.exceptionInMethodParameter(getMethodDescription(),
+                    index, ex), ex);
         }
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalClassTransformationImpl.java Wed Jul  7 18:28:11 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@ import org.apache.tapestry5.ioc.util.IdA
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.ComponentEvent;
 import org.apache.tapestry5.services.*;
 import org.slf4j.Logger;
 
@@ -335,8 +336,8 @@ public final class InternalClassTransfor
             String methodName = newMemberName("access", sig.getMethodName());
 
             TransformMethodSignature accessMethodSignature = new TransformMethodSignature(Modifier.PUBLIC
-                    + Modifier.STATIC, sig.getReturnType(), methodName, parameterTypes.toArray(new String[0]), sig
-                    .getExceptionTypes());
+                    + Modifier.STATIC, sig.getReturnType(), methodName, parameterTypes.toArray(new String[0]),
+                    sig.getExceptionTypes());
 
             boolean isVoid = sig.getReturnType().equals("void");
 
@@ -726,7 +727,7 @@ public final class InternalClassTransfor
 
     private final List<Object> constructorArgs;
 
-    private final ComponentModel componentModel;
+    private final MutableComponentModel componentModel;
 
     private final String resourcesFieldName;
 
@@ -758,7 +759,7 @@ public final class InternalClassTransfor
      * This is a constructor for a base class.
      */
     public InternalClassTransformationImpl(ClassFactory classFactory, CtClass ctClass,
-            ComponentClassCache componentClassCache, ComponentModel componentModel, CtClassSource classSource)
+            ComponentClassCache componentClassCache, MutableComponentModel componentModel, CtClassSource classSource)
     {
         this.ctClass = ctClass;
         this.componentClassCache = componentClassCache;
@@ -796,7 +797,7 @@ public final class InternalClassTransfor
      */
     private InternalClassTransformationImpl(CtClass ctClass, InternalClassTransformation parentTransformation,
             ClassFactory classFactory, CtClassSource classSource, ComponentClassCache componentClassCache,
-            ComponentModel componentModel)
+            MutableComponentModel componentModel)
     {
         this.ctClass = ctClass;
         this.componentClassCache = componentClassCache;
@@ -1877,8 +1878,8 @@ public final class InternalClassTransfor
             // Replace the constructor body with one that fails. This leaves, as an open question,
             // what to do about any other constructors.
 
-            String body = String.format("throw new RuntimeException(\"%s\");", ServicesMessages
-                    .forbidInstantiateComponentClass(getClassName()));
+            String body = String.format("throw new RuntimeException(\"%s\");",
+                    ServicesMessages.forbidInstantiateComponentClass(getClassName()));
 
             defaultConstructor.setBody(body);
         }
@@ -1911,15 +1912,15 @@ public final class InternalClassTransfor
         // Pass $1 (the InternalComponentResources object) and the constructorArgs (from the AbstractIntantiator
         // base class) into the new component instance's constructor
 
-        cf.addMethod(Modifier.PUBLIC, NEW_INSTANCE_SIGNATURE, String.format("return new %s($1, constructorArgs);",
-                componentClassName));
+        cf.addMethod(Modifier.PUBLIC, NEW_INSTANCE_SIGNATURE,
+                String.format("return new %s($1, constructorArgs);", componentClassName));
 
         Class instantiatorClass = cf.createClass();
 
         try
         {
-            Object instance = instantiatorClass.getConstructors()[0].newInstance(componentModel, String.format(
-                    "Instantiator[%s]", componentClassName), componentConstructorArgs);
+            Object instance = instantiatorClass.getConstructors()[0].newInstance(componentModel,
+                    String.format("Instantiator[%s]", componentClassName), componentConstructorArgs);
 
             return (Instantiator) instance;
         }
@@ -2125,8 +2126,8 @@ public final class InternalClassTransfor
                 String fieldName = access.getFieldName();
                 CtMethod method = (CtMethod) where;
 
-                formatter.format("Checking field %s %s in method %s(): ", isRead ? "read" : "write", fieldName, method
-                        .getName());
+                formatter.format("Checking field %s %s in method %s(): ", isRead ? "read" : "write", fieldName,
+                        method.getName());
 
                 // Ignore any methods to were added as part of the transformation.
                 // If we reference the field there, we really mean the field.
@@ -2306,4 +2307,41 @@ public final class InternalClassTransfor
         };
     }
 
+    public void addComponentEventHandler(String eventType, int minContextValues, String methodDescription,
+            ComponentEventHandler handler)
+    {
+        Defense.notBlank(eventType, "eventType");
+        Defense.notBlank(methodDescription, "methodDescription");
+        Defense.notNull(handler, "handler");
+
+        componentModel.addEventHandler(eventType);
+
+        getOrCreateMethod(TransformConstants.DISPATCH_COMPONENT_EVENT).addAdvice(
+                createEventHandlerAdvice(eventType, minContextValues, methodDescription, handler));
+
+    }
+
+    private static ComponentMethodAdvice createEventHandlerAdvice(final String eventType, final int minContextValues,
+            final String methodDescription, final ComponentEventHandler handler)
+    {
+        return new ComponentMethodAdvice()
+        {
+            public void advise(ComponentMethodInvocation invocation)
+            {
+                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
+
+                if (event.matches(eventType, "", minContextValues))
+                {
+                    event.setMethodDescription(methodDescription);
+
+                    handler.handleEvent(invocation.getInstance(), event);
+
+                    invocation.overrideResult(true);
+                }
+
+                if (!event.isAborted())
+                    invocation.proceed();
+            }
+        };
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ActivationRequestParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ActivationRequestParameterWorker.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ActivationRequestParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ActivationRequestParameterWorker.java Wed Jul  7 18:28:11 2010
@@ -33,6 +33,7 @@ import org.apache.tapestry5.services.*;
  * @see ActivationRequestParameter
  * @since 5.2.0
  */
+@SuppressWarnings("all")
 public class ActivationRequestParameterWorker implements ComponentClassTransformWorker
 {
     private final Request request;
@@ -41,11 +42,6 @@ public class ActivationRequestParameterW
 
     private final ValueEncoderSource valueEncoderSource;
 
-    interface EventHandler
-    {
-        void invoke(Component component, ComponentEvent event);
-    }
-
     public ActivationRequestParameterWorker(Request request, ComponentClassCache classCache,
             ValueEncoderSource valueEncoderSource)
     {
@@ -62,7 +58,6 @@ public class ActivationRequestParameterW
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void mapFieldToQueryParameter(TransformField field, ClassTransformation transformation,
             MutableComponentModel model)
     {
@@ -70,8 +65,6 @@ public class ActivationRequestParameterW
 
         String parameterName = getParameterName(field, annotation);
 
-        TransformMethod dispatchMethod = transformation.getOrCreateMethod(TransformConstants.DISPATCH_COMPONENT_EVENT);
-
         // Assumption: the field type is not one that's loaded by the component class loader, so it's safe
         // to convert to a hard type during class transformation.
 
@@ -81,16 +74,16 @@ public class ActivationRequestParameterW
 
         FieldAccess access = field.getAccess();
 
-        setValueFromInitializeEventHandler(access, parameterName, encoder, dispatchMethod, model);
-        decorateLinks(access, parameterName, encoder, dispatchMethod, model);
-        preallocateName(parameterName, dispatchMethod, model);
+        setValueFromInitializeEventHandler(transformation, access, parameterName, encoder);
+        decorateLinks(transformation, access, parameterName, encoder);
+        preallocateName(transformation, parameterName);
     }
 
-    private void preallocateName(final String parameterName, TransformMethod dispatchMethod, MutableComponentModel model)
+    private static void preallocateName(ClassTransformation transformation, final String parameterName)
     {
-        EventHandler handler = new EventHandler()
+        ComponentEventHandler handler = new ComponentEventHandler()
         {
-            public void invoke(Component component, ComponentEvent event)
+            public void handleEvent(Component instance, ComponentEvent event)
             {
                 IdAllocator idAllocator = event.getEventContext().get(IdAllocator.class, 0);
 
@@ -98,16 +91,18 @@ public class ActivationRequestParameterW
             }
         };
 
-        add(dispatchMethod, model, EventConstants.PREALLOCATE_FORM_CONTROL_NAMES, handler);
+        transformation.addComponentEventHandler(EventConstants.PREALLOCATE_FORM_CONTROL_NAMES, 1,
+                "ActivationRequestParameterWorker preallocate form control name '" + parameterName + "' event handler",
+                handler);
     }
 
-    @SuppressWarnings("unchecked")
-    private void setValueFromInitializeEventHandler(final FieldAccess access, final String parameterName,
-            final ValueEncoder encoder, TransformMethod dispatchMethod, MutableComponentModel model)
+    @SuppressWarnings("all")
+    private void setValueFromInitializeEventHandler(ClassTransformation transformation, final FieldAccess access,
+            final String parameterName, final ValueEncoder encoder)
     {
-        EventHandler handler = new EventHandler()
+        ComponentEventHandler handler = new ComponentEventHandler()
         {
-            public void invoke(Component component, ComponentEvent event)
+            public void handleEvent(Component instance, ComponentEvent event)
             {
                 String clientValue = request.getParameter(parameterName);
 
@@ -116,22 +111,23 @@ public class ActivationRequestParameterW
 
                 Object value = encoder.toValue(clientValue);
 
-                access.write(component, value);
+                access.write(instance, value);
             }
         };
 
-        add(dispatchMethod, model, EventConstants.ACTIVATE, handler);
+        transformation.addComponentEventHandler(EventConstants.ACTIVATE, 0,
+                "ActivationRequestParameterWorker activate event handler", handler);
     }
 
-    @SuppressWarnings("unchecked")
-    private void decorateLinks(final FieldAccess access, final String parameterName, final ValueEncoder encoder,
-            TransformMethod dispatchMethod, MutableComponentModel model)
+    @SuppressWarnings("all")
+    private static void decorateLinks(ClassTransformation transformation, final FieldAccess access,
+            final String parameterName, final ValueEncoder encoder)
     {
-        EventHandler handler = new EventHandler()
+        ComponentEventHandler handler = new ComponentEventHandler()
         {
-            public void invoke(Component component, ComponentEvent event)
+            public void handleEvent(Component instance, ComponentEvent event)
             {
-                Object value = access.read(component);
+                Object value = access.read(instance);
 
                 if (value == null)
                     return;
@@ -144,29 +140,11 @@ public class ActivationRequestParameterW
             }
         };
 
-        add(dispatchMethod, model, EventConstants.DECORATE_COMPONENT_EVENT_LINK, handler);
-        add(dispatchMethod, model, EventConstants.DECORATE_PAGE_RENDER_LINK, handler);
-    }
-
-    private void add(TransformMethod dispatchMethod, MutableComponentModel model, final String eventType,
-            final EventHandler handler)
-    {
-        dispatchMethod.addAdvice(new ComponentMethodAdvice()
-        {
-            public void advise(ComponentMethodInvocation invocation)
-            {
-                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
-
-                if (event.matches(eventType, "", 0))
-                {
-                    handler.invoke(invocation.getInstance(), event);
-                }
-
-                invocation.proceed();
-            }
-        });
+        transformation.addComponentEventHandler(EventConstants.DECORATE_COMPONENT_EVENT_LINK, 0,
+                "ActivationRequestParameterWorker decorate component event link event handler", handler);
 
-        model.addEventHandler(eventType);
+        transformation.addComponentEventHandler(EventConstants.DECORATE_PAGE_RENDER_LINK, 0,
+                "ActivationRequestParameterWorker decorate page render link event handler", handler);
     }
 
     private String getParameterName(TransformField field, ActivationRequestParameter annotation)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java Wed Jul  7 18:28:11 2010
@@ -149,8 +149,8 @@ public class OnEventWorker implements Co
 
                 for (EventHandlerMethodInvoker invoker : invokers)
                 {
-                    if (event.matches(invoker.getEventType(), invoker.getComponentId(), invoker
-                            .getMinContextValueCount()))
+                    if (event.matches(invoker.getEventType(), invoker.getComponentId(),
+                            invoker.getMinContextValueCount()))
                     {
                         didInvokeSomeHandler = true;
 
@@ -290,21 +290,19 @@ public class OnEventWorker implements Co
 
                     if (parameterType.isPrimitive() && value == null)
                         throw new RuntimeException(
-                                String
-                                        .format(
-                                                "Query parameter '%s' evaluates to null, but the event method parameter is type %s, a primitive.",
-                                                parameterName, parameterType.getName()));
+                                String.format(
+                                        "Query parameter '%s' evaluates to null, but the event method parameter is type %s, a primitive.",
+                                        parameterName, parameterType.getName()));
 
                     return value;
                 }
                 catch (Exception ex)
                 {
                     throw new RuntimeException(
-                            String
-                                    .format(
-                                            "Unable process query parameter '%s' as parameter #%d of event handler method %s (in class %s): %s",
-                                            parameterName, parameterIndex + 1, signature, componentClassName,
-                                            InternalUtils.toMessage(ex)), ex);
+                            String.format(
+                                    "Unable process query parameter '%s' as parameter #%d of event handler method %s (in class %s): %s",
+                                    parameterName, parameterIndex + 1, signature, componentClassName,
+                                    InternalUtils.toMessage(ex)), ex);
                 }
             }
         };

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageActivationContextWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageActivationContextWorker.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageActivationContextWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageActivationContextWorker.java Wed Jul  7 18:28:11 2010
@@ -19,9 +19,11 @@ import java.util.List;
 import org.apache.tapestry5.EventConstants;
 import org.apache.tapestry5.annotations.PageActivationContext;
 import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.ComponentEvent;
 import org.apache.tapestry5.services.ClassTransformation;
 import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.ComponentEventHandler;
 import org.apache.tapestry5.services.ComponentMethodAdvice;
 import org.apache.tapestry5.services.ComponentMethodInvocation;
 import org.apache.tapestry5.services.FieldAccess;
@@ -59,76 +61,46 @@ public class PageActivationContextWorker
     {
         PageActivationContext annotation = field.getAnnotation(PageActivationContext.class);
 
-        TransformMethod dispatchEventMethod = transformation
-                .getOrCreateMethod(TransformConstants.DISPATCH_COMPONENT_EVENT);
-
         FieldAccess access = field.getAccess();
 
         if (annotation.activate())
         {
-            dispatchEventMethod.addAdvice(createActivateAdvice(field.getType(), access));
-            model.addEventHandler(EventConstants.ACTIVATE);
+            transformation.addComponentEventHandler(EventConstants.ACTIVATE, 1,
+                    "PageActivationContextWorker activate event handler",
+                    createActivationHandler(field.getType(), access));
         }
 
         if (annotation.passivate())
         {
-            dispatchEventMethod.addAdvice(createPassivateAdvice(access));
-            model.addEventHandler(EventConstants.PASSIVATE);
+            transformation.addComponentEventHandler(EventConstants.PASSIVATE, 0,
+                    "PageActivationContextWorker passivate event handler", createPassivateHandler(access));
         }
 
         // We don't claim the field, and other workers may even replace it with a FieldValueConduit.
     }
 
-    private static ComponentMethodAdvice createActivateAdvice(final String fieldType, final FieldAccess access)
+    private static ComponentEventHandler createActivationHandler(final String fieldType, final FieldAccess access)
     {
-        return new ComponentMethodAdvice()
+        return new ComponentEventHandler()
         {
-            public void advise(ComponentMethodInvocation invocation)
+            public void handleEvent(Component instance, ComponentEvent event)
             {
-                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
-
-                if (event.isAborted())
-                    return;
-
-                if (event.matches(EventConstants.ACTIVATE, "", 1))
-                {
-                    event.setMethodDescription(access.toString());
-
-                    Object value = event.coerceContext(0, fieldType);
-
-                    access.write(invocation.getInstance(), value);
-
-                    invocation.overrideResult(true);
-                }
+                Object value = event.coerceContext(0, fieldType);
 
-                invocation.proceed();
+                access.write(instance, value);
             }
         };
     }
 
-    private static ComponentMethodAdvice createPassivateAdvice(final FieldAccess access)
+    private static ComponentEventHandler createPassivateHandler(final FieldAccess access)
     {
-        return new ComponentMethodAdvice()
+        return new ComponentEventHandler()
         {
-            public void advise(ComponentMethodInvocation invocation)
+            public void handleEvent(Component instance, ComponentEvent event)
             {
-                ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
-
-                if (event.isAborted())
-                    return;
-
-                if (event.matches(EventConstants.PASSIVATE, "", 0))
-                {
-                    event.setMethodDescription(access.toString());
-
-                    Object value = access.read(invocation.getInstance());
-
-                    event.storeResult(value);
-
-                    invocation.overrideResult(true);
-                }
+                Object value = access.read(instance);
 
-                invocation.proceed();
+                event.storeResult(value);
             }
         };
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/runtime/ComponentEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/runtime/ComponentEvent.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/runtime/ComponentEvent.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/runtime/ComponentEvent.java Wed Jul  7 18:28:11 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
 //
 // Licensed 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
+// 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,
@@ -20,28 +20,33 @@ import org.apache.tapestry5.EventContext
 /**
  * An event that may originate in application logic, or as a result of a client interaction (a GET or POST from the
  * client).
- *
+ * 
  * @see ComponentResourcesCommon#triggerEvent(String, Object[], org.apache.tapestry5.ComponentEventCallback)
  * @see org.apache.tapestry5.ComponentEventCallback
  */
 public interface ComponentEvent extends Event
 {
     /**
-     * Returns true if the event matches the provided criteria.
-     *
-     * @param eventType      the type of event (case insensitive match)
-     * @param componentId    component is to match against (case insensitive), or the empty string
-     * @param parameterCount minimum number of context values
-     * @return true if the event matches.
+     * Returns true if the event matches the provided criteria and the event has not yet been aborted.
+     * 
+     * @param eventType
+     *            the type of event (case insensitive match)
+     * @param componentId
+     *            component is to match against (case insensitive), or the empty string
+     * @param parameterCount
+     *            minimum number of context values
+     * @return true if the event matches (and has not yet been aborted)
      */
     boolean matches(String eventType, String componentId, int parameterCount);
 
     /**
      * Coerces a context value to a particular type. The context is an array of objects; typically it is an array of
      * strings of extra path information encoded into the action URL.
-     *
-     * @param index           the index of the context value
-     * @param desiredTypeName the desired type
+     * 
+     * @param index
+     *            the index of the context value
+     * @param desiredTypeName
+     *            the desired type
      * @return the coerced value (a wrapper type if the desired type is a primitive)
      */
     Object coerceContext(int index, String desiredTypeName);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ClassTransformation.java Wed Jul  7 18:28:11 2010
@@ -23,6 +23,9 @@ import org.apache.tapestry5.ComponentRes
 import org.apache.tapestry5.func.Predicate;
 import org.apache.tapestry5.internal.transform.ReadOnlyFieldValueConduit;
 import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.Event;
 import org.slf4j.Logger;
 
 /**
@@ -616,4 +619,24 @@ public interface ClassTransformation ext
      * @return true if a such a method exists
      */
     boolean isDeclaredMethod(TransformMethodSignature signature);
+
+    /**
+     * Adds advice to the {@link Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)} method.
+     * If the handler is invoked,
+     * the return value of the method will be overriden to true. The invocation will proceeed either way (whether the
+     * handler is invoked or not). Updates {@linkplain MutableComponentModel#addEventHandler(String) the model} to
+     * indicate that there is a handler for the named event.
+     * 
+     * @param eventType
+     *            name of event to be handled
+     * @param minContextValues
+     *            minimum number of event context values required to invoke the method
+     * @param methodDescription
+     *            Text description of what the handler does (used with {@link Event#setMethodDescription(String)})
+     * @param handler
+     *            the handler to invoke
+     * @since 5.2.0
+     */
+    void addComponentEventHandler(String eventType, int minContextValues, String methodDescription,
+            ComponentEventHandler handler);
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java?rev=961463&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java Wed Jul  7 18:28:11 2010
@@ -0,0 +1,29 @@
+// Copyright 2010 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.services;
+
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.runtime.ComponentEvent;
+
+/**
+ * Interface used with {@link ClassTransformation#addComponentEventHandler(String, int, String, ComponentEventHandler)}.
+ * 
+ * @since 5.2.0
+ */
+public interface ComponentEventHandler
+{
+    /** Handles the event. */
+    void handleEvent(Component instance, ComponentEvent event);
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java?rev=961463&r1=961462&r2=961463&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java Wed Jul  7 18:28:11 2010
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2010 The Apache Software Foundation
 //
 // Licensed 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
+// 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,
@@ -66,6 +66,31 @@ public class ComponentEventImplTest exte
         verify();
     }
 
+    /** @since 5.2.0 */
+    @Test
+    public void no_match_one_event_is_aborted()
+    {
+        ComponentEventCallback handler = mockComponentEventHandler();
+        EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+        Object result = new Object();
+
+        train_isDebugEnabled(logger, false);
+
+        train_handleResult(handler, result, true);
+
+        replay();
+
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
+
+        event.storeResult(result);
+
+        assertFalse(event.matches("eventType", "someId", 0));
+
+        verify();
+
+    }
+
     @Test
     public void event_type_match_is_case_insensitive()
     {
@@ -178,7 +203,7 @@ public class ComponentEventImplTest exte
         catch (IllegalArgumentException ex)
         {
             assertEquals(ex.getMessage(),
-                         "Method foo.Bar.baz() has more parameters than there are context values for this component event.");
+                    "Method foo.Bar.baz() has more parameters than there are context values for this component event.");
         }
 
         verify();
@@ -334,8 +359,7 @@ public class ComponentEventImplTest exte
         }
         catch (IllegalStateException ex)
         {
-            assertEquals(ex.getMessage(), ServicesMessages
-                    .componentEventIsAborted("foo.Bar.biff()"));
+            assertEquals(ex.getMessage(), ServicesMessages.componentEventIsAborted("foo.Bar.biff()"));
         }
 
         verify();