Author: hlship
Date: Sat Jul 23 16:57:20 2011
New Revision: 1150146
URL: http://svn.apache.org/viewvc?rev=1150146&view=rev
Log:
TAP5-1508: Convert OnEventWorker from using advice on dispatchComponentEvent()
to directly invoking methods
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/BaseEventHandlerMethodInvoker.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodInvoker.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java?rev=1150146&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/EventHandlerMethodParameterSource.java
Sat Jul 23 16:57:20 2011
@@ -0,0 +1,58 @@
+//
+// Copyright 2011 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.internal.transform;
+
+import org.apache.tapestry5.ioc.Invokable;
+import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.runtime.ComponentEvent;
+
+/**
+ * Used to encapsulate the list of {@link
EventHandlerMethodParameterProvider}s for a particular
+ * method of a particular component, providing {@link OperationTracker}
behavior as parameter values
+ * are obtained/computed/coerced.
+ *
+ * @since 5.3
+ */
+public class EventHandlerMethodParameterSource
+{
+ private final String methodIdentifier;
+
+ private final OperationTracker operationTracker;
+
+ private final EventHandlerMethodParameterProvider[] providers;
+
+ public EventHandlerMethodParameterSource(String methodIdentifier,
OperationTracker operationTracker, EventHandlerMethodParameterProvider[]
providers)
+ {
+
+ this.methodIdentifier = methodIdentifier;
+ this.operationTracker = operationTracker;
+ this.providers = providers;
+ }
+
+ public Object get(final ComponentEvent event, final int index)
+ {
+ // Hopefully this will not be too much overhead; it's really nice to
be able to track what parameter
+ // caused a failure.
+
+ return operationTracker.invoke(String.format("Obtaining value for
parameter #%d of %s", index + 1, methodIdentifier),
+ new Invokable<Object>()
+ {
+ public Object invoke()
+ {
+ return
providers[index].valueForEventHandlerMethodParameter(event);
+ }
+ });
+ }
+}
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=1150146&r1=1150145&r2=1150146&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
Sat Jul 23 16:57:20 2011
@@ -14,35 +14,31 @@
package org.apache.tapestry5.internal.transform;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.RequestParameter;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Flow;
-import org.apache.tapestry5.func.Mapper;
import org.apache.tapestry5.func.Predicate;
-import org.apache.tapestry5.func.Worker;
import org.apache.tapestry5.internal.services.ComponentClassCache;
+import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.plastic.MethodAdvice;
-import org.apache.tapestry5.plastic.MethodDescription;
-import org.apache.tapestry5.plastic.MethodInvocation;
-import org.apache.tapestry5.plastic.PlasticClass;
-import org.apache.tapestry5.plastic.PlasticMethod;
+import org.apache.tapestry5.plastic.*;
import org.apache.tapestry5.runtime.ComponentEvent;
+import org.apache.tapestry5.runtime.Event;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.TransformConstants;
import org.apache.tapestry5.services.ValueEncoderSource;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.apache.tapestry5.services.transform.TransformationSupport;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
/**
* Provides implementations of the
* {@link
org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
@@ -56,6 +52,150 @@ public class OnEventWorker implements Co
private final ComponentClassCache classCache;
+ private final OperationTracker operationTracker;
+
+ private final InstructionBuilderCallback RETURN_TRUE = new
InstructionBuilderCallback()
+ {
+ public void doBuild(InstructionBuilder builder)
+ {
+ builder.loadConstant(true).returnResult();
+ }
+ };
+
+ /**
+ * Encapsulates information needed to invoke a method as an event handler
method, including the logic
+ * to construct parameter values, and match the method against the {@link
ComponentEvent}.
+ */
+ class EventHandlerMethod
+ {
+ final PlasticMethod method;
+
+ final MethodDescription description;
+
+ final String eventType, componentId;
+
+ final EventHandlerMethodParameterSource parameterSource;
+
+ int minContextValues = 0;
+
+ EventHandlerMethod(PlasticMethod method)
+ {
+ this.method = method;
+ description = method.getDescription();
+
+ parameterSource = buildSource();
+
+ String methodName = method.getDescription().methodName;
+
+ OnEvent onEvent = method.getAnnotation(OnEvent.class);
+
+ eventType = extractEventType(methodName, onEvent);
+ componentId = extractComponentId(methodName, onEvent);
+ }
+
+ void buildMatchAndInvocation(InstructionBuilder builder, final
LocalVariable resultVariable)
+ {
+ final PlasticField sourceField =
+ parameterSource == null ? null
+ :
method.getPlasticClass().introduceField(EventHandlerMethodParameterSource.class,
description.methodName + "$parameterSource").inject(parameterSource);
+
+
builder.loadArgument(0).loadConstant(eventType).loadConstant(componentId).loadConstant(minContextValues);
+ builder.invoke(ComponentEvent.class, boolean.class, "matches",
String.class, String.class, int.class);
+
+ builder.when(Condition.NON_ZERO, new InstructionBuilderCallback()
+ {
+ public void doBuild(InstructionBuilder builder)
+ {
+
builder.loadArgument(0).loadConstant(method.getMethodIdentifier()).invoke(Event.class,
void.class, "setMethodDescription", String.class);
+
+ builder.loadThis();
+
+ int count = description.argumentTypes.length;
+
+ for (int i = 0; i < count; i++)
+ {
+
builder.loadThis().getField(sourceField).loadArgument(0).loadConstant(i);
+
+
builder.invoke(EventHandlerMethodParameterSource.class, Object.class, "get",
+ ComponentEvent.class, int.class);
+
+ builder.castOrUnbox(description.argumentTypes[i]);
+ }
+
+ builder.invokeVirtual(method);
+
+ if (!method.isVoid())
+ {
+ builder.boxPrimitive(description.returnType);
+ builder.loadArgument(0).swap();
+
+ builder.invoke(Event.class, boolean.class,
"storeResult", Object.class);
+
+ // storeResult() returns true if the method is
aborted. Return true since, certainly,
+ // a method was invoked.
+ builder.when(Condition.NON_ZERO, RETURN_TRUE);
+ }
+
+ // Set the result to true, to indicate that some method
was invoked.
+
+ builder.loadConstant(true).storeVariable(resultVariable);
+ }
+ });
+ }
+
+
+ private EventHandlerMethodParameterSource buildSource()
+ {
+ final String[] parameterTypes =
method.getDescription().argumentTypes;
+
+ if (parameterTypes.length == 0)
+ {
+ return null;
+ }
+
+ final List<EventHandlerMethodParameterProvider> providers =
CollectionFactory.newList();
+
+ int contextIndex = 0;
+
+ for (int i = 0; i < parameterTypes.length; i++)
+ {
+ String type = parameterTypes[i];
+
+ EventHandlerMethodParameterProvider provider =
parameterTypeToProvider.get(type);
+
+ if (provider != null)
+ {
+ providers.add(provider);
+ continue;
+ }
+
+ RequestParameter parameterAnnotation =
method.getParameters().get(i).getAnnotation(RequestParameter.class);
+
+ if (parameterAnnotation != null)
+ {
+ String parameterName = parameterAnnotation.value();
+
+ providers.add(createQueryParameterProvider(method, i,
parameterName, type,
+ parameterAnnotation.allowBlank()));
+ continue;
+ }
+
+ // Note: probably safe to do the conversion to Class early
(class load time)
+ // as parameters are rarely (if ever) component classes.
+
+ providers.add(createEventContextProvider(type,
contextIndex++));
+ }
+
+
+ minContextValues = contextIndex;
+
+ EventHandlerMethodParameterProvider[] providerArray =
providers.toArray(new EventHandlerMethodParameterProvider[providers.size()]);
+
+ return new
EventHandlerMethodParameterSource(method.getMethodIdentifier(),
operationTracker, providerArray);
+ }
+ }
+
+
/**
* Stores a couple of special parameter type mappings that are used when
matching the entire event context
* (either as Object[] or EventContext).
@@ -94,11 +234,12 @@ public class OnEventWorker implements Co
});
}
- public OnEventWorker(Request request, ValueEncoderSource
valueEncoderSource, ComponentClassCache classCache)
+ public OnEventWorker(Request request, ValueEncoderSource
valueEncoderSource, ComponentClassCache classCache, OperationTracker
operationTracker)
{
this.request = request;
this.valueEncoderSource = valueEncoderSource;
this.classCache = classCache;
+ this.operationTracker = operationTracker;
}
public void transform(PlasticClass plasticClass, TransformationSupport
support, MutableComponentModel model)
@@ -106,78 +247,52 @@ public class OnEventWorker implements Co
Flow<PlasticMethod> methods = matchEventHandlerMethods(plasticClass);
if (methods.isEmpty())
+ {
return;
+ }
- Flow<EventHandlerMethodInvoker> invokers =
toInvokers(plasticClass.getClassName(), methods);
-
- updateModelWithHandledEvents(model, invokers);
-
- adviseDispatchComponentEventMethod(plasticClass, invokers);
+ implementDispatchComponentEvent(plasticClass,
support.isRootTransformation(), methods, model);
}
- private void adviseDispatchComponentEventMethod(PlasticClass plasticClass,
Flow<EventHandlerMethodInvoker> invokers)
- {
- MethodAdvice advice = createDispatchComponentEventAdvice(invokers);
-
-
plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION).addAdvice(advice);
- }
- private MethodAdvice
createDispatchComponentEventAdvice(Flow<EventHandlerMethodInvoker> invokers)
+ private void implementDispatchComponentEvent(final PlasticClass
plasticClass, final boolean isRoot, final Flow<PlasticMethod> eventMethods,
final MutableComponentModel model)
{
- final EventHandlerMethodInvoker[] invokersArray =
invokers.toArray(EventHandlerMethodInvoker.class);
-
- return new MethodAdvice()
+
plasticClass.introduceMethod(TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION).changeImplementation(new
InstructionBuilderCallback()
{
- public void advise(MethodInvocation invocation)
- {
- // Invoke the super-class implementation first. If no
super-class,
- // this will do nothing and return false.
-
- invocation.proceed();
-
- ComponentEvent event = (ComponentEvent)
invocation.getParameter(0);
-
- if (invokeEventHandlers(event, invocation.getInstance()))
- invocation.setReturnValue(true);
- }
-
- private boolean invokeEventHandlers(ComponentEvent event, Object
instance)
+ public void doBuild(InstructionBuilder builder)
{
- // If the super-class aborted the event (some super-class
method return non-null),
- // then it's all over, don't even check for handlers in this
class.
-
- if (event.isAborted())
- return false;
-
- boolean didInvokeSomeHandler = false;
-
- for (EventHandlerMethodInvoker invoker : invokersArray)
+ builder.startVariable("boolean", new LocalVariableCallback()
{
- if (event.matches(invoker.getEventType(),
invoker.getComponentId(),
- invoker.getMinContextValueCount()))
+ public void doBuild(LocalVariable resultVariable,
InstructionBuilder builder)
{
- didInvokeSomeHandler = true;
+ if (!isRoot)
+ {
+ // As a subclass, there will be a base class
implementation (possibly empty).
+
+
builder.loadThis().loadArguments().invokeSpecial(plasticClass.getSuperClassName(),
TransformConstants.DISPATCH_COMPONENT_EVENT_DESCRIPTION);
+
+ // First store the result of the super() call into
the variable.
+ builder.storeVariable(resultVariable);
+ builder.loadArgument(0).invoke(Event.class,
boolean.class, "isAborted");
+ builder.when(Condition.NON_ZERO, RETURN_TRUE);
+ } else
+ {
+ // No event handler method has yet been invoked.
+
builder.loadConstant(false).storeVariable(resultVariable);
+ }
+
+ for (PlasticMethod method : eventMethods)
+ {
+ EventHandlerMethod eventHandlerMethod = new
EventHandlerMethod(method);
- invoker.invokeEventHandlerMethod(event, instance);
+
eventHandlerMethod.buildMatchAndInvocation(builder, resultVariable);
- if (event.isAborted())
- break;
- }
- }
+
model.addEventHandler(eventHandlerMethod.eventType);
+ }
- return didInvokeSomeHandler;
- }
- };
- }
-
- private void updateModelWithHandledEvents(final MutableComponentModel
model,
- Flow<EventHandlerMethodInvoker> invokers)
- {
- invokers.each(new Worker<EventHandlerMethodInvoker>()
- {
- public void work(EventHandlerMethodInvoker value)
- {
- model.addEventHandler(value.getEventType());
+ builder.loadVariable(resultVariable).returnResult();
+ }
+ });
}
});
}
@@ -200,80 +315,12 @@ public class OnEventWorker implements Co
{
return method.hasAnnotation(OnEvent.class);
}
-
});
}
- private Flow<EventHandlerMethodInvoker> toInvokers(final String
componentClassName, Flow<PlasticMethod> methods)
- {
- return methods.map(new Mapper<PlasticMethod,
EventHandlerMethodInvoker>()
- {
- public EventHandlerMethodInvoker map(PlasticMethod element)
- {
- return toInvoker(componentClassName, element);
- }
- });
- }
-
- private EventHandlerMethodInvoker toInvoker(final String
componentClassName, PlasticMethod method)
- {
- OnEvent annotation = method.getAnnotation(OnEvent.class);
-
- final MethodDescription description = method.getDescription();
-
- String methodName = description.methodName;
- String eventType = extractEventType(methodName, annotation);
- String componentId = extractComponentId(methodName, annotation);
-
- String[] parameterTypes = description.argumentTypes;
-
- if (parameterTypes.length == 0)
- return new BaseEventHandlerMethodInvoker(method, eventType,
componentId);
-
- final List<EventHandlerMethodParameterProvider> providers =
CollectionFactory.newList();
-
- // I'd refactor a bit more of this if Java had covariant return types.
-
- int contextIndex = 0;
-
- for (int i = 0; i < parameterTypes.length; i++)
- {
- String type = parameterTypes[i];
-
- EventHandlerMethodParameterProvider provider =
parameterTypeToProvider.get(type);
-
- if (provider != null)
- {
- providers.add(provider);
- continue;
- }
-
- RequestParameter parameterAnnotation =
method.getParameters().get(i).getAnnotation(RequestParameter.class);
-
- if (parameterAnnotation != null)
- {
- String parameterName = parameterAnnotation.value();
-
- providers.add(createQueryParameterSource(componentClassName,
description, i, parameterName, type,
- parameterAnnotation.allowBlank()));
- continue;
- }
-
- // Note: probably safe to do the conversion to Class early (class
load time)
- // as parameters are rarely (if ever) component classes.
-
- final int parameterIndex = contextIndex++;
-
- providers.add(createEventContextSource(type, parameterIndex));
- }
-
- return createInvoker(method, eventType, componentId, contextIndex,
providers);
- }
-
- private EventHandlerMethodParameterProvider
createQueryParameterSource(final String componentClassName,
- final MethodDescription description, final int parameterIndex,
final String parameterName,
- final String parameterTypeName, final boolean allowBlank)
+ private EventHandlerMethodParameterProvider
createQueryParameterProvider(final PlasticMethod method, final int
parameterIndex, final String parameterName,
+
final String parameterTypeName, final boolean allowBlank)
{
return new EventHandlerMethodParameterProvider()
{
@@ -302,48 +349,19 @@ public class OnEventWorker implements Co
parameterName,
parameterType.getName()));
return value;
- }
- catch (Exception ex)
+ } 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,
description, componentClassName,
+ "Unable process query parameter '%s' as
parameter #%d of event handler method %s: %s",
+ parameterName, parameterIndex + 1,
method.getMethodIdentifier(),
InternalUtils.toMessage(ex)), ex);
}
}
};
}
- private EventHandlerMethodInvoker createInvoker(PlasticMethod method,
String eventType, String componentId,
- final int minContextCount, final
List<EventHandlerMethodParameterProvider> providers)
- {
- return new BaseEventHandlerMethodInvoker(method, eventType,
componentId)
- {
- final int count = providers.size();
-
- @Override
- public int getMinContextValueCount()
- {
- return minContextCount;
- }
-
- @Override
- protected Object[] constructParameters(ComponentEvent event)
- {
- Object[] parameters = new Object[count];
-
- for (int i = 0; i < count; i++)
- {
- parameters[i] =
providers.get(i).valueForEventHandlerMethodParameter(event);
- }
-
- return parameters;
- }
- };
- }
-
- private EventHandlerMethodParameterProvider createEventContextSource(final
String type, final int parameterIndex)
+ private EventHandlerMethodParameterProvider
createEventContextProvider(final String type, final int parameterIndex)
{
return new EventHandlerMethodParameterProvider()
{