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 2008/04/26 05:00:58 UTC
svn commit: r651797 [1/2] - in /tapestry/tapestry5/trunk:
tapestry-core/src/main/java/org/apache/tapestry/internal/services/
tapestry-core/src/main/java/org/apache/tapestry/services/
tapestry-core/src/site/apt/ tapestry-core/src/test/app1/ tapestry-cor...
Author: hlship
Date: Fri Apr 25 20:00:55 2008
New Revision: 651797
URL: http://svn.apache.org/viewvc?rev=651797&view=rev
Log:
TAPESTRY-2389: Add support to ClassTransformation to allow ComponentClassTransformWorkers to provide advice to methods
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AbstractComponentMethodInvocation.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationBuilder.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InvocationBuilder.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MethodInvocationInfo.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodAdvice.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodInvocation.java
tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MethodAdviceDemo.tml
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MethodAdviceDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/DearGodWhyMeException.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStrings.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStringsWorker.java
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/upgrade.apt
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCache.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCacheImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/AppModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ApplicationStateWorkerTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ParameterWorkerTest.java
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/CommitAfterWorker.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/Invocation.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/AspectInterceptorBuilderImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/AspectDecorator.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/ClassFabUtils.java
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/site.xml
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry/ioc/services/ClassFabUtilsTest.java
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AbstractComponentMethodInvocation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AbstractComponentMethodInvocation.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AbstractComponentMethodInvocation.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AbstractComponentMethodInvocation.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,144 @@
+// Copyright 2008 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.tapestry.internal.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.services.ComponentMethodAdvice;
+import org.apache.tapestry.services.ComponentMethodInvocation;
+
+public abstract class AbstractComponentMethodInvocation implements ComponentMethodInvocation
+{
+ private final MethodInvocationInfo _info;
+
+ private final ComponentResources _resources;
+
+ private int _adviceIndex = 0;
+
+ private Throwable _thrown;
+
+ private Object _result;
+
+ public AbstractComponentMethodInvocation(MethodInvocationInfo info, ComponentResources resources)
+ {
+ _info = info;
+ _resources = resources;
+ }
+
+ public ComponentResources getComponentResources()
+ {
+ return _resources;
+ }
+
+ public String getMethodName()
+ {
+ return _info.getMethodName();
+ }
+
+ public Class getResultType()
+ {
+ return _info.getResultType();
+ }
+
+ public int getParameterCount()
+ {
+ return _info.getParameterCount();
+ }
+
+ public Class getParameterType(int index)
+ {
+ return _info.getParameterType(index);
+ }
+
+ /**
+ * This first call is to the first advice. When we run out of advice, we re-invoke.
+ */
+ public void proceed()
+ {
+ if (_adviceIndex >= _info.getAdviceCount())
+ {
+ invokeAdvisedMethod();
+ return;
+ }
+
+ ComponentMethodAdvice advice = _info.getAdvice(_adviceIndex++);
+
+ // When this advice invokes proceed(), we can advance to the next advice,
+ // and then ultimately to the advised method.
+
+ advice.advise(this);
+ }
+
+ /**
+ * Implemented to reinvoke the method on the advised method of the component.
+ */
+ protected abstract void invokeAdvisedMethod();
+
+ public boolean isFail()
+ {
+ return _thrown != null;
+ }
+
+ public <T extends Throwable> T getThrown(Class<T> throwableClass)
+ {
+ if (throwableClass.isInstance(_thrown))
+ return throwableClass.cast(_thrown);
+
+ return null;
+ }
+
+ public void overrideThrown(Exception thrown)
+ {
+ for (Class type : _info.getExceptionTypes())
+ {
+ if (type.isInstance(thrown))
+ {
+ _thrown = thrown;
+ return;
+ }
+ }
+
+ throw new IllegalArgumentException(
+ String.format("Exception class %s is not a declared exception type for method %s().",
+ thrown.getClass(),
+ _info.getMethodName()));
+ }
+
+ public Object getResult()
+ {
+ return _result;
+ }
+
+ public void overrideResult(Object newResult)
+ {
+ if (newResult != null)
+ {
+ Class expectedType = _info.getEffectiveResultType();
+
+ if (!expectedType.isInstance(newResult))
+ {
+ throw new IllegalArgumentException(
+ String.format("Invalid result value (%s) does not match return type %s for method %s.",
+ newResult,
+ expectedType.getName(),
+ _info.getMethodName()));
+ }
+ }
+
+ _result = newResult;
+ _thrown = null;
+ }
+
+
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCache.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCache.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCache.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCache.java Fri Apr 25 20:00:55 2008
@@ -21,9 +21,9 @@
public interface ComponentClassCache
{
/**
- * Gets the Class instance for then give name.
+ * Gets the Class instance for the given fully-qualified class name.
*
- * @param className fully qualified class name, or an primtive type name, or an array name (in source format)
+ * @param className fully qualified class name, or a primitive type name, or an array name (in source format)
* @return the class instance
*/
Class forName(String className);
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCacheImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCacheImpl.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCacheImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassCacheImpl.java Fri Apr 25 20:00:55 2008
@@ -44,24 +44,35 @@
if (result == null)
{
- // This step is necessary to handle primitives and, especially, primitive arrays.
+ result = lookupClassForType(className);
- String jvmName = ClassFabUtils.toJVMBinaryName(className);
-
- ClassLoader componentLoader = _classFactory.getClassLoader();
-
- try
- {
- result = Class.forName(jvmName, true, componentLoader);
- }
- catch (ClassNotFoundException ex)
- {
- throw new RuntimeException(ex);
- }
_cache.put(className, result);
}
return result;
+ }
+
+ private Class lookupClassForType(String className)
+ {
+ if (className.equals("void")) return void.class;
+
+ if (ClassFabUtils.isPrimitiveType(className))
+ return ClassFabUtils.getPrimitiveType(className);
+
+ // This step is necessary to handle primitives arrays.
+
+ String jvmName = ClassFabUtils.toJVMBinaryName(className);
+
+ ClassLoader componentLoader = _classFactory.getClassLoader();
+
+ try
+ {
+ return Class.forName(jvmName, true, componentLoader);
+ }
+ catch (ClassNotFoundException ex)
+ {
+ throw new RuntimeException(ex);
+ }
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java Fri Apr 25 20:00:55 2008
@@ -52,20 +52,25 @@
private final ClassFactory _classFactory;
+ private final ComponentClassCache _componentClassCache;
+
private final String[] SUBPACKAGES = { "." + InternalConstants.PAGES_SUBPACKAGE + ".",
"." + InternalConstants.COMPONENTS_SUBPACKAGE + ".",
"." + InternalConstants.MIXINS_SUBPACKAGE + ".",
"." + InternalConstants.BASE_SUBPACKAGE + "." };
/**
- * @param workerChain the ordered list of class transform works as a chain of command instance
+ * @param workerChain the ordered list of class transform works as a chain of command instance
+ * @param componentClassCache
*/
public ComponentClassTransformerImpl(ComponentClassTransformWorker workerChain, LoggerSource loggerSource,
- @ComponentLayer ClassFactory classFactory)
+ @ComponentLayer ClassFactory classFactory,
+ ComponentClassCache componentClassCache)
{
_workerChain = workerChain;
_loggerSource = loggerSource;
_classFactory = classFactory;
+ _componentClassCache = componentClassCache;
}
/**
@@ -81,7 +86,7 @@
{
String parentClassname;
-// Component classes must be public
+ // Component classes must be public
if (!Modifier.isPublic(ctClass.getModifiers())) return;
@@ -113,8 +118,8 @@
Logger logger = _loggerSource.getLogger(classname);
-// If the parent class is in a controlled package, it will already have been loaded and
-// transformed (that is driven by the ComponentInstantiatorSource).
+ // If the parent class is in a controlled package, it will already have been loaded and
+ // transformed (that is driven by the ComponentInstantiatorSource).
InternalClassTransformation parentTransformation = _nameToClassTransformation
.get(parentClassname);
@@ -136,11 +141,10 @@
MutableComponentModel model = new MutableComponentModelImpl(classname, logger, baseResource, parentModel);
- InternalClassTransformation transformation = parentTransformation == null ? new InternalClassTransformationImpl(
- ctClass, _classFactory, logger, model) : new InternalClassTransformationImpl(ctClass,
- parentTransformation,
- _classFactory, logger,
- model);
+ InternalClassTransformation transformation =
+ parentTransformation == null
+ ? new InternalClassTransformationImpl(_classFactory, _componentClassCache, ctClass, model)
+ : parentTransformation.createChildTransformation(ctClass, model);
try
{
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationBuilder.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationBuilder.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/ComponentInvocationBuilder.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,33 @@
+// Copyright 2008 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.tapestry.internal.services;
+
+import org.apache.tapestry.ioc.MethodAdvice;
+
+public interface ComponentInvocationBuilder
+{
+ /**
+ * Adds advice to the method invocation.
+ *
+ * @param advice
+ */
+ void addAdvice(MethodAdvice advice);
+
+ /**
+ * Finalize the method invocation, creating the final ComponentMethodInvocation class and rewiring the component
+ * class to make use of it.
+ */
+ void commit();
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformation.java Fri Apr 25 20:00:55 2008
@@ -14,10 +14,13 @@
package org.apache.tapestry.internal.services;
+import javassist.CtClass;
import org.apache.tapestry.internal.util.MultiKey;
import org.apache.tapestry.ioc.internal.util.IdAllocator;
+import org.apache.tapestry.model.MutableComponentModel;
import org.apache.tapestry.services.ClassTransformation;
import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.TransformMethodSignature;
import java.util.List;
@@ -55,6 +58,21 @@
/**
* Searchs for an existing injection of an object, returning the name of the protected field into which the value
* was injected.
+ * <p/>
+ * TODO: Howard sayz: Uggh! At least define a real key (MultiKey is intended for internal use, never part of an
+ * API). Is this necessary? The cost of re-injection is tiny.
*/
String searchForPreviousInjection(MultiKey key);
+
+ InternalClassTransformation createChildTransformation(CtClass childClass, MutableComponentModel childModel);
+
+ /**
+ * Creates a new method by copying the body of an existing method. This is part of the scheme for providing method
+ * advice.
+ *
+ * @param sourceMethod method to be copied
+ * @param modifiers modifiers for the new method
+ * @param newMethodName name of new method to create
+ */
+ void copyMethod(TransformMethodSignature sourceMethod, int modifiers, String newMethodName);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InternalClassTransformationImpl.java Fri Apr 25 20:00:55 2008
@@ -22,6 +22,7 @@
import org.apache.tapestry.internal.util.MultiKey;
import org.apache.tapestry.ioc.internal.util.CollectionFactory;
import static org.apache.tapestry.ioc.internal.util.CollectionFactory.*;
+import org.apache.tapestry.ioc.internal.util.Defense;
import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
import org.apache.tapestry.ioc.internal.util.IdAllocator;
@@ -32,11 +33,9 @@
import org.apache.tapestry.ioc.services.MethodSignature;
import org.apache.tapestry.ioc.util.BodyBuilder;
import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.model.MutableComponentModel;
import org.apache.tapestry.runtime.Component;
-import org.apache.tapestry.services.FieldFilter;
-import org.apache.tapestry.services.MethodFilter;
-import org.apache.tapestry.services.TransformMethodSignature;
-import org.apache.tapestry.services.TransformUtils;
+import org.apache.tapestry.services.*;
import org.slf4j.Logger;
import static java.lang.String.format;
@@ -90,6 +89,8 @@
private Map<CtMethod, TransformMethodSignature> _methodSignatures = newMap();
+ private Map<TransformMethodSignature, InvocationBuilder> _methodToInvocationBuilder = CollectionFactory.newMap();
+
// Key is field name, value is expression used to replace read access
private Map<String, String> _fieldReadTransforms;
@@ -116,6 +117,8 @@
private final ClassFactory _classFactory;
+ private final ComponentClassCache _componentClassCache;
+
/**
* Signature for newInstance() method of Instantiator.
*/
@@ -127,10 +130,11 @@
/**
* This is a constructor for a base class.
*/
- public InternalClassTransformationImpl(CtClass ctClass, ClassFactory classFactory, Logger logger,
- ComponentModel componentModel)
+ public InternalClassTransformationImpl(ClassFactory classFactory, ComponentClassCache componentClassCache,
+ CtClass ctClass, ComponentModel componentModel)
{
_ctClass = ctClass;
+ _componentClassCache = componentClassCache;
_classPool = _ctClass.getClassPool();
_classFactory = classFactory;
_parentTransformation = null;
@@ -138,7 +142,7 @@
_idAllocator = new IdAllocator();
- _logger = logger;
+ _logger = componentModel.getLogger();
preloadMemberNames();
@@ -161,13 +165,15 @@
/**
* Constructor for a component sub-class.
*/
- public InternalClassTransformationImpl(CtClass ctClass, InternalClassTransformation parentTransformation,
- ClassFactory classFactory, Logger logger, ComponentModel componentModel)
+ private InternalClassTransformationImpl(CtClass ctClass, InternalClassTransformation parentTransformation,
+ ClassFactory classFactory, ComponentClassCache componentClassCache,
+ ComponentModel componentModel)
{
_ctClass = ctClass;
+ _componentClassCache = componentClassCache;
_classPool = _ctClass.getClassPool();
_classFactory = classFactory;
- _logger = logger;
+ _logger = componentModel.getLogger();
_parentTransformation = parentTransformation;
_componentModel = componentModel;
@@ -203,6 +209,11 @@
// The "}" will be added later, inside finish().
}
+ public InternalClassTransformation createChildTransformation(CtClass childClass, MutableComponentModel childModel)
+ {
+ return new InternalClassTransformationImpl(childClass, this, _classFactory, _componentClassCache, childModel);
+ }
+
private void freeze()
{
_frozen = true;
@@ -224,6 +235,7 @@
_formatter = null;
// _ctClass = null; -- needed by toString()
_classPool = null;
+ _methodToInvocationBuilder = null;
}
public String getResourcesFieldName()
@@ -673,6 +685,44 @@
addMethodToDescription("extend existing", methodSignature, methodBody);
}
+ public void copyMethod(TransformMethodSignature sourceMethod, int modifiers, String newMethodName)
+ {
+ failIfFrozen();
+
+ CtClass returnType = findCtClass(sourceMethod.getReturnType());
+ CtClass[] parameters = buildCtClassList(sourceMethod.getParameterTypes());
+ CtClass[] exceptions = buildCtClassList(sourceMethod.getExceptionTypes());
+ CtMethod source = findMethod(sourceMethod);
+
+ try
+ {
+ CtMethod method = new CtMethod(returnType, newMethodName, parameters, _ctClass);
+
+ method.setModifiers(modifiers);
+
+ method.setExceptionTypes(exceptions);
+
+ method.setBody(source, null);
+
+ _ctClass.addMethod(method);
+
+ }
+ catch (CannotCompileException ex)
+ {
+ throw new RuntimeException(String.format("Error copying method %s to new method %s().",
+ sourceMethod,
+ newMethodName), ex);
+ }
+ catch (NotFoundException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+
+ // The new method is *not* considered an added method, so field references inside the method
+ // will be transformed.
+
+ _formatter.format("\n%s renamed to %s\n\n", sourceMethod, newMethodName);
+ }
public void addCatch(TransformMethodSignature methodSignature, String exceptionType, String body)
{
@@ -1150,6 +1200,22 @@
return null;
}
+ public void advise(TransformMethodSignature methodSignature, ComponentMethodAdvice advice)
+ {
+ Defense.notNull(methodSignature, "methodSignature");
+ Defense.notNull(advice, "advice");
+
+ InvocationBuilder builder = _methodToInvocationBuilder.get(methodSignature);
+
+ if (builder == null)
+ {
+ builder = new InvocationBuilder(this, _classFactory, _componentClassCache, methodSignature);
+ _methodToInvocationBuilder.put(methodSignature, builder);
+ }
+
+ builder.addAdvice(advice);
+ }
+
/**
* Adds a parameter to the constructor for the class; the parameter is used to initialize the value for a field.
*
@@ -1186,6 +1252,11 @@
{
failIfFrozen();
+ for (InvocationBuilder builder : _methodToInvocationBuilder.values())
+ {
+ builder.commit();
+ }
+
performFieldTransformations();
addConstructor();
@@ -1316,16 +1387,14 @@
// $1 is model, $2 is description, to $3 is first dynamic parameter.
- constructor.add("%s = $%d", fieldName, i + 2);
+ // The arguments may be wrapper types, so we cast down to
+ // the primitive type.
- if (primitive)
- {
- String methodName = ClassFabUtils.getUnwrapMethodName(argType);
-
- constructor.add(".%s()", methodName);
- }
+ String parameterReference = "$" + (i + 2);
- constructor.addln(";");
+ constructor.addln("%s = %s;",
+ fieldName,
+ ClassFabUtils.castReference(parameterReference, fieldType.getName()));
newInstance.add(", %s", fieldName);
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InvocationBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InvocationBuilder.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InvocationBuilder.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/InvocationBuilder.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,282 @@
+// Copyright 2008 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.tapestry.internal.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.ClassFabUtils;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.ioc.util.BodyBuilder;
+import org.apache.tapestry.services.ComponentMethodAdvice;
+import org.apache.tapestry.services.TransformMethodSignature;
+
+import java.lang.reflect.Modifier;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Used by {@link org.apache.tapestry.internal.services.InternalClassTransformationImpl} to manage adding method
+ * invocation advice to arbitrary methods.
+ */
+public class InvocationBuilder
+{
+ private static final String FIELD_NAME = "_p";
+
+ private static final MethodSignature OVERRIDE_SIGNATURE =
+ new MethodSignature(void.class, "override", new Class[] { int.class, Object.class }, null);
+
+ private static final MethodSignature GET_PARAMETER_SIGNATURE =
+ new MethodSignature(Object.class, "getParameter", new Class[] { int.class }, null);
+
+ private static final MethodSignature INVOKE_ADVISED_METHOD_SIGNATURE =
+ new MethodSignature(void.class, "invokeAdvisedMethod", null, null);
+
+ private final InternalClassTransformation _transformation;
+
+ private final ClassFactory _classFactory;
+
+ private final TransformMethodSignature _methodSignature;
+
+ private final MethodInvocationInfo _info;
+
+ private static final AtomicLong UID_GENERATOR = new AtomicLong(System.currentTimeMillis());
+
+ private static final int PROTECTED_FINAL = Modifier.PROTECTED | Modifier.FINAL;
+
+ private static final int PUBLIC_FINAL = Modifier.PUBLIC | Modifier.FINAL;
+
+ private static String nextUID()
+ {
+ return Long.toHexString(UID_GENERATOR.getAndIncrement());
+ }
+
+ public InvocationBuilder(InternalClassTransformation transformation, ClassFactory classFactory,
+ ComponentClassCache componentClassCache, TransformMethodSignature methodSignature)
+ {
+ _transformation = transformation;
+ _classFactory = classFactory;
+ _methodSignature = methodSignature;
+
+ _info = new MethodInvocationInfo(methodSignature, componentClassCache);
+ }
+
+ public void addAdvice(ComponentMethodAdvice advice)
+ {
+ _info.addAdvice(advice);
+ }
+
+ public void commit()
+ {
+ // The class name is the component class name plus the method name plus a unique uid. This places
+ // the invocation in the same package as the component class; the original method will ultimately
+ // be renamed and modified to be package private.
+
+ String className =
+ _transformation.getClassName() + "$" + _methodSignature.getMethodName() + "$invocation_" + nextUID();
+
+ ClassFab invocationFab = _classFactory.newClass(className, AbstractComponentMethodInvocation.class);
+
+ createConstructor(invocationFab);
+
+ implementOverride(invocationFab);
+
+ implementGetParameter(invocationFab);
+
+ String renamed = copyAdvisedMethod();
+
+ implementInvokeAdviseMethod(invocationFab, renamed);
+
+ rebuildOriginalMethod(className);
+
+ // Force the creation of the class now
+
+ invocationFab.createClass();
+ }
+
+ private void rebuildOriginalMethod(String invocationClassName)
+ {
+ String methodInfoField = _transformation.addInjectedField(MethodInvocationInfo.class,
+ _methodSignature.getMethodName() + "Info",
+ _info);
+
+ String componentResourcesField = _transformation.getResourcesFieldName();
+
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ builder.addln("%s invocation = new %<s(%s, %s, $$);", invocationClassName, methodInfoField,
+ componentResourcesField);
+
+ // Off into the first MethodAdvice
+
+ builder.addln("invocation.proceed();");
+
+ String[] exceptionTypes = _methodSignature.getExceptionTypes();
+ int exceptionCount = exceptionTypes.length;
+
+ if (exceptionCount > 0)
+ {
+ for (int i = 0; i < exceptionCount; i++)
+ {
+ String type = exceptionTypes[i];
+ String name = "ex" + i;
+
+ builder.addln("%s %s = (%1$s) invocation.getThrown(%s.getExceptionType(%d));",
+ type, name, methodInfoField, i);
+ builder.addln("if (%s != null) throw %<s;", name);
+ }
+ }
+
+ String returnType = _methodSignature.getReturnType();
+
+ if (!returnType.equals("void"))
+ {
+ builder.addln("return %s;",
+ ClassFabUtils.castReference("invocation.getResult()", returnType));
+ }
+
+
+ builder.end();
+
+ /** Replace the original method with the new implementation. */
+ _transformation.addMethod(_methodSignature, builder.toString());
+ }
+
+ private void implementInvokeAdviseMethod(ClassFab classFab, String advisedMethodName)
+ {
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ boolean isVoid = _methodSignature.getReturnType().equals("void");
+
+ builder.addln("%s component = (%<s) getComponentResources().getComponent();", _transformation.getClassName());
+
+ String[] exceptionTypes = _methodSignature.getExceptionTypes();
+ int exceptionCount = exceptionTypes.length;
+
+ if (exceptionCount > 0)
+ builder.add("try").begin();
+
+ if (!isVoid) builder.add("overrideResult(($w) ");
+
+ builder.add("component.%s(", advisedMethodName);
+
+ for (int i = 0; i < _methodSignature.getParameterTypes().length; i++)
+ {
+ if (i > 0) builder.add(", ");
+
+ builder.add("%s%d", FIELD_NAME, i);
+ }
+
+ builder.add(")");
+
+ if (!isVoid) builder.add(")");
+
+ builder.addln(";");
+
+ if (exceptionCount > 0)
+ {
+ builder.end(); // try
+
+ for (int i = 0; i < exceptionCount; i++)
+ {
+ builder.addln("catch (%s ex) { overrideThrown(ex); }", exceptionTypes[i]);
+ }
+ }
+
+ builder.end();
+
+ classFab.addMethod(PROTECTED_FINAL, INVOKE_ADVISED_METHOD_SIGNATURE, builder.toString());
+ }
+
+ private String copyAdvisedMethod()
+ {
+ String newName = _transformation.newMemberName("advised$" + _methodSignature.getMethodName());
+
+ _transformation.copyMethod(_methodSignature, Modifier.FINAL, newName);
+
+ return newName;
+ }
+
+ private void createConstructor(ClassFab classFab)
+ {
+ int parameterCount = _info.getParameterCount();
+
+ Class[] parameterTypes = new Class[parameterCount + 2];
+
+ parameterTypes[0] = MethodInvocationInfo.class;
+ parameterTypes[1] = ComponentResources.class;
+
+ BodyBuilder builder = new BodyBuilder().begin().addln("super($1,$2);");
+
+ for (int i = 0; i < parameterCount; i++)
+ {
+ String name = FIELD_NAME + i;
+
+ Class parameterType = _info.getParameterType(i);
+
+ parameterTypes[2 + i] = parameterType;
+
+ classFab.addField(name, _info.getParameterType(i));
+
+ builder.addln("%s = $%d;", name, 3 + i);
+ }
+
+ builder.end();
+
+ classFab.addConstructor(parameterTypes, null, builder.toString());
+ }
+
+ private void implementOverride(ClassFab classFab)
+ {
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ builder.addln("switch ($1)").begin();
+
+ int count = _methodSignature.getParameterTypes().length;
+
+ for (int i = 0; i < count; i++)
+ {
+ String type = _methodSignature.getParameterTypes()[i];
+
+ builder.addln("case %d: %s = %s; break;", i, FIELD_NAME + i, ClassFabUtils.castReference("$2", type));
+ }
+
+ builder.addln("default: throw new IllegalArgumentException(\"Index out of range.\");");
+
+ builder.end().end();
+
+ classFab.addMethod(PUBLIC_FINAL, OVERRIDE_SIGNATURE, builder.toString());
+ }
+
+ private void implementGetParameter(ClassFab classFab)
+ {
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ builder.addln("switch ($1)").begin();
+
+ int count = _methodSignature.getParameterTypes().length;
+
+ for (int i = 0; i < count; i++)
+ {
+ builder.addln("case %d: return ($w) %s;", i, FIELD_NAME + i);
+ }
+
+ builder.addln("default: throw new IllegalArgumentException(\"Index out of range.\");");
+
+ builder.end().end();
+
+ classFab.addMethod(PUBLIC_FINAL, GET_PARAMETER_SIGNATURE, builder.toString());
+ }
+
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MethodInvocationInfo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MethodInvocationInfo.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MethodInvocationInfo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MethodInvocationInfo.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,114 @@
+// Copyright 2008 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.tapestry.internal.services;
+
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.ioc.services.ClassFabUtils;
+import org.apache.tapestry.services.ComponentMethodAdvice;
+import org.apache.tapestry.services.TransformMethodSignature;
+
+import java.util.List;
+
+/**
+ * A companion to {@link org.apache.tapestry.internal.services.AbstractComponentMethodInvocation} that stores most of
+ * the method and advice information needed.
+ */
+public class MethodInvocationInfo
+{
+ private final TransformMethodSignature _methodSignature;
+
+ private final ComponentClassCache _componentClassCache;
+
+ private final List<ComponentMethodAdvice> _advice = CollectionFactory.newList();
+
+ private Class _effectiveResultType;
+
+ public MethodInvocationInfo(TransformMethodSignature methodSignature, ComponentClassCache componentClassCache)
+ {
+ _methodSignature = methodSignature;
+ _componentClassCache = componentClassCache;
+ }
+
+ public String getMethodName()
+ {
+ return _methodSignature.getMethodName();
+ }
+
+ public Class getResultType()
+ {
+ return _componentClassCache.forName(_methodSignature.getReturnType());
+ }
+
+ public synchronized Class getEffectiveResultType()
+ {
+ if (_effectiveResultType == null)
+ {
+ Class resultType = getResultType();
+
+ _effectiveResultType = resultType.isPrimitive() ? ClassFabUtils.getWrapperType(resultType) : resultType;
+ }
+
+ return _effectiveResultType;
+ }
+
+ public int getParameterCount()
+ {
+ return _methodSignature.getParameterTypes().length;
+ }
+
+ public Class getParameterType(int index)
+ {
+ return _componentClassCache.forName(_methodSignature.getParameterTypes()[index]);
+ }
+
+ public int getAdviceCount()
+ {
+ return _advice.size();
+ }
+
+ public ComponentMethodAdvice getAdvice(int index)
+ {
+ return _advice.get(index);
+ }
+
+ public void addAdvice(ComponentMethodAdvice advice)
+ {
+ // Ultimately, the mutable portion of this object's lifecycle all occurs inside a synchronized block defined by
+ // the class loader. After that the _advice list is only accessed for reads. I don't think there
+ // are any concurrency issues with this approach.
+
+ _advice.add(advice);
+ }
+
+ public Class[] getExceptionTypes()
+ {
+ String[] exceptionTypes = _methodSignature.getExceptionTypes();
+ int count = exceptionTypes.length;
+
+ Class[] result = new Class[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ result[i] = _componentClassCache.forName(exceptionTypes[i]);
+ }
+
+ return result;
+ }
+
+ public Class getExceptionType(int index)
+ {
+ return _componentClassCache.forName(_methodSignature.getExceptionTypes()[index]);
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PropertyConduitSourceImpl.java Fri Apr 25 20:00:55 2008
@@ -363,22 +363,16 @@
return null;
}
- Class parameterType = writeMethod.getParameterTypes()[0];
-
- Class wrapperType = ClassFabUtils.getWrapperType(parameterType);
+ Class propertyType = writeMethod.getParameterTypes()[0];
+ String propertyTypeName = ClassFabUtils.toJavaClassName(propertyType);
// Cast the parameter from Object to the expected type for the method.
- builder.addln("%s value = (%<s) $2;", ClassFabUtils.toJavaClassName(wrapperType));
-
- // Invoke the method, possibly converting a wrapper type to a primitive type along the way.
-
- builder.add("%s.%s(value", result.getFinalStepVariable(), writeMethod.getName());
+ builder.addln("%s value = %s;", propertyTypeName, ClassFabUtils.castReference("$2", propertyTypeName));
- if (parameterType != wrapperType)
- builder.add(".%s()", ClassFabUtils.getUnwrapMethodName(parameterType.getName()));
+ // Invoke the method.
- builder.addln(");");
+ builder.addln("%s.%s(value);", result.getFinalStepVariable(), writeMethod.getName());
builder.end();
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ClassTransformation.java Fri Apr 25 20:00:55 2008
@@ -357,7 +357,7 @@
* Returns true if this transformation represents a root class (one that extends directly from Object), or false if
* this transformation is an extension of another transformed class.
*
- * @return true if root class, false is sub-class
+ * @return true if root class, false if sub-class
*/
boolean isRootTransformation();
@@ -371,4 +371,9 @@
* @param body code to execute
*/
void addCatch(TransformMethodSignature methodSignature, String exceptionType, String body);
+
+ /**
+ * Adds method advice for the indicated method.
+ */
+ void advise(TransformMethodSignature methodSignature, ComponentMethodAdvice advice);
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodAdvice.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodAdvice.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodAdvice.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodAdvice.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,27 @@
+// Copyright 2008 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.tapestry.services;
+
+/**
+ * An object that receives control around an "advised" method of a component. The advise can query or even replace
+ * method parameters. After invoking {@link org.apache.tapestry.services.ComponentMethodInvocation#proceed()}, the
+ * advise may query and override thrown exceptions or the return value of the invocation.
+ *
+ * @see ClassTransformation#advise(TransformMethodSignature, ComponentMethodAdvice)
+ */
+public interface ComponentMethodAdvice
+{
+ void advise(ComponentMethodInvocation invocation);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodInvocation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodInvocation.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodInvocation.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/ComponentMethodInvocation.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,30 @@
+// Copyright 2008 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.tapestry.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.ioc.Invocation;
+
+/**
+ * Encapsulates the parameters, thrown exceptions, and result of a method invocation, allowing a {@link
+ * org.apache.tapestry.services.ComponentMethodAdvice} to encapsulate the invocation.
+ */
+public interface ComponentMethodInvocation extends Invocation
+{
+ /**
+ * Returns the component resources for the component whose method is being intercepted.
+ */
+ ComponentResources getComponentResources();
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/site/apt/index.apt Fri Apr 25 20:00:55 2008
@@ -10,6 +10,9 @@
New And Of Note
+ * It is now possible to provide method invocation advice to component methods. This opens up
+ a very powerful Aspect Oriented Programming approach to Tapestry components.
+
* The Exception Report page now identifies the version of the Tapestry framework, and lists
out System properties (including the Java classpath).
@@ -58,7 +61,6 @@
* The new "var:" binding prefix allows for temporary, untyped storage
of render-time values without having to define a new component property.
-
Changes from Tapestry 4 to Tapestry 5
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MethodAdviceDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MethodAdviceDemo.tml?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MethodAdviceDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/MethodAdviceDemo.tml Fri Apr 25 20:00:55 2008
@@ -0,0 +1,33 @@
+<html t:type="Border"
+ xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+ <h1>Method Advice Demo</h1>
+
+ <p>
+ Greetings and salutations:
+ <span id="message">${message}</span>.
+ </p>
+
+
+ <p>
+ The current Major version of Tapestry is:
+ <span id="version">${version}</span>
+ </p>
+
+
+ <t:beaneditform object="this"/>
+
+ <t:if test="text">
+ <p>
+ Stored text:
+ <span id="output-text">${text}</span>
+ </p>
+ </t:if>
+
+ <p>
+ Cranky:
+ <span id="cranky">${cranky}</span>
+ </p>
+
+
+</html>
\ No newline at end of file
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/IntegrationTests.java Fri Apr 25 20:00:55 2008
@@ -1995,4 +1995,28 @@
assertEquals(getText("lastupdate"), timestamp,
"Timestamp should not have changed because updates are in-place.");
}
+
+
+ @Test
+ public void method_advice()
+ {
+ start("Method Advice Demo");
+
+ // @ReverseStrings intercepted and reversed the result:
+ assertText("message", "!olleH");
+
+ // @ReverseStrings doesn't do anything for non-Strings
+ assertText("version", "5");
+
+ // @ReverseStrings filtered the checked exception to a string result
+ assertText("cranky",
+ "Invocation of method getCranky() failed with org.apache.tapestry.integration.app1.services.DearGodWhyMeException.");
+
+ // Now to check advice on a setter that manipulates parameters
+
+ type("text", "Tapestry");
+ clickAndWait(SUBMIT);
+
+ assertText("output-text", "yrtsepaT");
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MethodAdviceDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MethodAdviceDemo.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MethodAdviceDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/MethodAdviceDemo.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,57 @@
+// Copyright 2008 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.tapestry.integration.app1.pages;
+
+import org.apache.tapestry.annotations.Persist;
+import org.apache.tapestry.beaneditor.Validate;
+import org.apache.tapestry.integration.app1.services.DearGodWhyMeException;
+import org.apache.tapestry.integration.app1.services.ReverseStrings;
+
+public class MethodAdviceDemo
+{
+ @Persist
+ private String _text;
+
+ @Validate("required")
+ public String getText()
+ {
+ return _text;
+ }
+
+ @ReverseStrings
+ public void setText(String text)
+ {
+ _text = text;
+ }
+
+ @ReverseStrings
+ public String getMessage()
+ {
+ return "Hello!";
+ }
+
+ @ReverseStrings
+ public int getVersion()
+ {
+ return 5;
+ }
+
+ @ReverseStrings
+ public String getCranky() throws DearGodWhyMeException
+ {
+ throw new DearGodWhyMeException();
+ }
+
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/pages/Start.java Fri Apr 25 20:00:55 2008
@@ -259,7 +259,9 @@
new Item("cachedpage2", "Cached Annotation2", "Caching method return values w/ inheritence"),
- new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax")
+ new Item("inplacegriddemo", "In-Place Grid Demo", "Grid that updates in-place using Ajax"),
+
+ new Item("methodadvicedemo", "Method Advice Demo", "Advising component methods.")
);
static
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/AppModule.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/AppModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/AppModule.java Fri Apr 25 20:00:55 2008
@@ -232,4 +232,10 @@
configuration.add(Track.class, GenericValueEncoderFactory.create(encoder));
}
+
+ public static void contributeComponentClassTransformWorker(
+ OrderedConfiguration<ComponentClassTransformWorker> configuration)
+ {
+ configuration.add("ReverseStringsWorker", new ReverseStringsWorker());
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/DearGodWhyMeException.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/DearGodWhyMeException.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/DearGodWhyMeException.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/DearGodWhyMeException.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,19 @@
+// Copyright 2008 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.tapestry.integration.app1.services;
+
+public class DearGodWhyMeException extends Exception
+{
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStrings.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStrings.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStrings.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStrings.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,31 @@
+// Copyright 2008 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.tapestry.integration.app1.services;
+
+import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.METHOD;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation used with {@link org.apache.tapestry.integration.app1.services.StringReversalAdvice}.
+ */
+@Target(METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ReverseStrings
+{
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStringsWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStringsWorker.java?rev=651797&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStringsWorker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/integration/app1/services/ReverseStringsWorker.java Fri Apr 25 20:00:55 2008
@@ -0,0 +1,79 @@
+// Copyright 2008 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.tapestry.integration.app1.services;
+
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.*;
+
+public class ReverseStringsWorker implements ComponentClassTransformWorker
+{
+ private final ComponentMethodAdvice _advice = new ComponentMethodAdvice()
+ {
+ public void advise(ComponentMethodInvocation invocation)
+ {
+ for (int i = 0; i < invocation.getParameterCount(); i++)
+ {
+ if (invocation.getParameterType(i).equals(String.class))
+ {
+ String value = (String) invocation.getParameter(i);
+
+ invocation.override(i, reverse(value));
+ }
+ }
+
+ invocation.proceed();
+
+ if (invocation.getResultType().equals(String.class))
+ {
+ if (invocation.isFail())
+ {
+ Exception thrown = invocation.getThrown(Exception.class);
+
+ invocation.overrideResult(
+ String.format("Invocation of method %s() failed with %s.",
+ invocation.getMethodName(),
+ thrown.getClass().getName()));
+
+ return;
+ }
+
+ String value = (String) invocation.getResult();
+
+ invocation.overrideResult(reverse(value));
+ }
+ }
+
+ private String reverse(String input)
+ {
+ char[] inputArray = input.toCharArray();
+ char[] outputArray = new char[inputArray.length];
+
+ for (int i = 0; i < inputArray.length; i++)
+ {
+ outputArray[inputArray.length - i - 1] = inputArray[i];
+ }
+
+ return new String(outputArray);
+ }
+ };
+
+ public void transform(ClassTransformation transformation, MutableComponentModel model)
+ {
+ for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(ReverseStrings.class))
+ {
+ transformation.advise(sig, _advice);
+ }
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/services/InternalClassTransformationImplTest.java Fri Apr 25 20:00:55 2008
@@ -20,6 +20,7 @@
import org.apache.tapestry.annotations.Retain;
import org.apache.tapestry.annotations.SetupRender;
import org.apache.tapestry.internal.InternalComponentResources;
+import org.apache.tapestry.internal.model.MutableComponentModelImpl;
import org.apache.tapestry.internal.test.InternalBaseTestCase;
import org.apache.tapestry.internal.transform.FieldRemoval;
import org.apache.tapestry.internal.transform.InheritedAnnotation;
@@ -30,6 +31,7 @@
import org.apache.tapestry.ioc.services.ClassFactory;
import org.apache.tapestry.ioc.services.PropertyAccess;
import org.apache.tapestry.ioc.util.BodyBuilder;
+import org.apache.tapestry.model.MutableComponentModel;
import org.apache.tapestry.runtime.Component;
import org.apache.tapestry.runtime.ComponentResourcesAware;
import org.apache.tapestry.services.ClassTransformation;
@@ -169,7 +171,9 @@
{
CtClass ctClass = findCtClass(targetClass);
- return new InternalClassTransformationImpl(ctClass, _classFactory, logger, null);
+ MutableComponentModel model = new MutableComponentModelImpl("unknown-class", logger, null, null);
+
+ return new InternalClassTransformationImpl(_classFactory, null, ctClass, model);
}
@Test
@@ -475,11 +479,12 @@
CtClass targetObjectCtClass = findCtClass(TargetObject.class);
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
// Default behavior is to add an injected field for the InternalComponentResources object,
// so we'll just check that.
@@ -505,11 +510,14 @@
CtClass targetObjectCtClass = findCtClass(TargetObject.class);
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel();
+
+ train_getLogger(model, logger);
replay();
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
String parentFieldName = ct.addInjectedField(String.class, "_value", value);
@@ -522,7 +530,7 @@
CtClass subclassCtClass = findCtClass(TargetObjectSubclass.class);
- ct = new InternalClassTransformationImpl(subclassCtClass, ct, _classFactory, logger, null);
+ ct = ct.createChildTransformation(subclassCtClass, model);
String subclassFieldName = ct.addInjectedField(String.class, "_childValue", value);
@@ -537,7 +545,6 @@
ct.finish();
-
Instantiator instantiator = ct.createInstantiator();
Object instance = instantiator.newInstance(resources);
@@ -557,11 +564,12 @@
CtClass targetObjectCtClass = findCtClass(TargetObject.class);
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
ct.addImplementedInterface(FooInterface.class);
ct.addImplementedInterface(GetterMethodsInterface.class);
@@ -602,13 +610,14 @@
InternalComponentResources resources = mockInternalComponentResources();
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
ct.makeReadOnly("_value");
@@ -636,12 +645,13 @@
public void removed_fields_should_not_show_up_as_unclaimed() throws Exception
{
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(RemoveFieldBean.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, null, logger, null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(null, null, targetObjectCtClass, model);
ct.removeField("_barney");
@@ -656,13 +666,14 @@
InternalComponentResources resources = mockInternalComponentResources();
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
ct.extendConstructor("_value = \"from constructor\";");
@@ -681,13 +692,14 @@
InternalComponentResources resources = mockInternalComponentResources();
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(ReadOnlyBean.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
ct.injectField("_value", "Tapestry");
@@ -723,13 +735,14 @@
InternalComponentResources resources = mockInternalComponentResources();
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(FieldAccessBean.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
replaceAccessToField(ct, "foo");
replaceAccessToField(ct, "bar");
@@ -1110,13 +1123,14 @@
public void remove_field() throws Exception
{
Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
replay();
CtClass targetObjectCtClass = findCtClass(FieldRemoval.class);
- InternalClassTransformation ct = new InternalClassTransformationImpl(targetObjectCtClass, _classFactory, logger,
- null);
+ InternalClassTransformation ct = new InternalClassTransformationImpl(_classFactory, null, targetObjectCtClass,
+ model);
ct.removeField("_fieldToRemove");
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Fri Apr 25 20:00:55 2008
@@ -36,6 +36,7 @@
import org.apache.tapestry.ioc.services.SymbolProvider;
import org.apache.tapestry.model.ComponentModel;
import org.apache.tapestry.model.EmbeddedComponentModel;
+import org.apache.tapestry.model.MutableComponentModel;
import org.apache.tapestry.runtime.Component;
import org.apache.tapestry.runtime.RenderQueue;
import org.apache.tapestry.services.ComponentClassResolver;
@@ -643,5 +644,13 @@
protected final ClientBehaviorSupport mockClientBehaviorSupport()
{
return newMock(ClientBehaviorSupport.class);
+ }
+
+ protected final MutableComponentModel mockMutableComponentModel(Logger logger)
+ {
+ MutableComponentModel model = mockMutableComponentModel();
+ train_getLogger(model, logger);
+
+ return model;
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ApplicationStateWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ApplicationStateWorkerTest.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ApplicationStateWorkerTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ApplicationStateWorkerTest.java Fri Apr 25 20:00:55 2008
@@ -117,16 +117,18 @@
InternalComponentResources resources = mockInternalComponentResources();
ComponentClassCache cache = mockComponentClassCache();
+ train_getLogger(model, logger);
+
Class asoClass = SimpleASO.class;
CtClass ctClass = findCtClass(StateHolder.class);
- InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass, _classFactory, logger,
- null);
train_forName(cache, asoClass);
replay();
+ InternalClassTransformation transformation = new InternalClassTransformationImpl(_classFactory, null, ctClass,
+ model);
new ApplicationStateWorker(manager, cache).transform(transformation, model);
verify();
@@ -182,16 +184,18 @@
InternalComponentResources resources = mockInternalComponentResources();
ComponentClassCache cache = mockComponentClassCache();
+ train_getLogger(model, logger);
+
Class asoClass = SimpleASO.class;
CtClass ctClass = findCtClass(MaybeStateHolder.class);
- InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass, _classFactory, logger,
- null);
train_forName(cache, asoClass);
replay();
+ InternalClassTransformation transformation = new InternalClassTransformationImpl(_classFactory, null, ctClass,
+ model);
new ApplicationStateWorker(manager, cache).transform(transformation, model);
verify();
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ParameterWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ParameterWorkerTest.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ParameterWorkerTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry/internal/transform/ParameterWorkerTest.java Fri Apr 25 20:00:55 2008
@@ -464,7 +464,7 @@
String boundValue = "howdy!";
final Logger logger = mockLogger();
- MutableComponentModel model = mockMutableComponentModel();
+ MutableComponentModel model = mockMutableComponentModel(logger);
model.addParameter("value", false, TapestryConstants.PROP_BINDING_PREFIX);
@@ -509,7 +509,7 @@
String boundValue = "yowza!";
final Logger logger = mockLogger();
- MutableComponentModel model = mockMutableComponentModel();
+ MutableComponentModel model = mockMutableComponentModel(logger);
model.addParameter("value", false, TapestryConstants.PROP_BINDING_PREFIX);
@@ -560,7 +560,8 @@
*/
private Component setupForIntegrationTest(final InternalComponentResources resources) throws Exception
{
- MutableComponentModel model = mockMutableComponentModel();
+ final Logger logger = mockLogger();
+ MutableComponentModel model = mockMutableComponentModel(logger);
model.addParameter("invariantObject", false, TapestryConstants.PROP_BINDING_PREFIX);
model.addParameter("invariantPrimitive", false, TapestryConstants.PROP_BINDING_PREFIX);
@@ -568,7 +569,6 @@
model.addParameter("primitive", true, TapestryConstants.PROP_BINDING_PREFIX);
model.addParameter("uncached", false, TapestryConstants.LITERAL_BINDING_PREFIX);
- final Logger logger = mockLogger();
Runnable phaseTwoTraining = new Runnable()
{
@@ -599,9 +599,10 @@
ClassFactory cf = new ClassFactoryImpl(loader, pool, logger);
CtClass ctClass = pool.get(componentClassName);
- InternalClassTransformation transformation = new InternalClassTransformationImpl(ctClass, cf, logger, null);
replay();
+
+ InternalClassTransformation transformation = new InternalClassTransformationImpl(cf, null, ctClass, model);
new ParameterWorker(source).transform(transformation, model);
Modified: tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/CommitAfterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/CommitAfterWorker.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/CommitAfterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry/internal/hibernate/CommitAfterWorker.java Fri Apr 25 20:00:55 2008
@@ -16,11 +16,8 @@
import org.apache.tapestry.hibernate.HibernateSessionManager;
import org.apache.tapestry.hibernate.annotations.CommitAfter;
-import org.apache.tapestry.ioc.util.BodyBuilder;
import org.apache.tapestry.model.MutableComponentModel;
-import org.apache.tapestry.services.ClassTransformation;
-import org.apache.tapestry.services.ComponentClassTransformWorker;
-import org.apache.tapestry.services.TransformMethodSignature;
+import org.apache.tapestry.services.*;
/**
* Searches for methods that have the {@link org.apache.tapestry.hibernate.annotations.CommitAfter} annotation and adds
@@ -31,6 +28,27 @@
{
private final HibernateSessionManager _manager;
+ private final ComponentMethodAdvice _advice = new ComponentMethodAdvice()
+ {
+ public void advise(ComponentMethodInvocation invocation)
+ {
+ try
+ {
+ invocation.proceed();
+
+ // Success or checked exception:
+
+ _manager.commit();
+ }
+ catch (RuntimeException ex)
+ {
+ _manager.abort();
+
+ throw ex;
+ }
+ }
+ };
+
public CommitAfterWorker(HibernateSessionManager manager)
{
_manager = manager;
@@ -40,36 +58,7 @@
{
for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(CommitAfter.class))
{
- addCommitAbortLogic(sig, transformation);
- }
- }
-
- private void addCommitAbortLogic(TransformMethodSignature method, ClassTransformation transformation)
- {
- String managerField = transformation.addInjectedField(HibernateSessionManager.class, "manager", _manager);
-
- // Handle the normal case, a succesful method invocation.
-
- transformation.extendExistingMethod(method, String.format("%s.commit();", managerField));
-
- // Now, abort on any RuntimeException
-
- BodyBuilder builder = new BodyBuilder().begin().addln("%s.abort();", managerField);
-
- builder.addln("throw $e;").end();
-
- transformation.addCatch(method, RuntimeException.class.getName(), builder.toString());
-
- // Now, a commit for each thrown exception
-
- builder.clear();
- builder.begin().addln("%s.commit();", managerField).addln("throw $e;").end();
-
- String body = builder.toString();
-
- for (String name : method.getExceptionTypes())
- {
- transformation.addCatch(method, name, body);
+ transformation.advise(sig, _advice);
}
}
}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/Invocation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/Invocation.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/Invocation.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/Invocation.java Fri Apr 25 20:00:55 2008
@@ -15,7 +15,7 @@
package org.apache.tapestry.ioc;
/**
- * A method invocation passed to an {@link MethodAdvice}.
+ * A method invocation passed to an {@link org.apache.tapestry.ioc.MethodAdvice}.
*/
public interface Invocation
{
@@ -25,7 +25,8 @@
String getMethodName();
/**
- * Returns the type of the method result, which may be a primtive type (i.e., int.class) or even void (void.class).
+ * Returns the type of the method result, which may be a primitive type (i.e., int.class) or even void
+ * (void.class).
*/
Class getResultType();
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/AspectInterceptorBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/AspectInterceptorBuilderImpl.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/AspectInterceptorBuilderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/internal/services/AspectInterceptorBuilderImpl.java Fri Apr 25 20:00:55 2008
@@ -280,24 +280,11 @@
for (int i = 0; i < parameterTypes.length; i++)
{
Class type = parameterTypes[i];
+ String typeName = ClassFabUtils.toJavaClassName(type);
- builder.add("case %d: %s%d = ", i, PARAMETER_FIELD, i);
-
- if (type.isPrimitive())
- {
- Class wrapperType = ClassFabUtils.getWrapperType(type);
-
- builder.add("((%s)$2).%s()", wrapperType.getName(),
- ClassFabUtils.getUnwrapMethodName(wrapperType));
- }
- else
- {
- builder.add("(%s)$2", ClassFabUtils.toJavaClassName(type));
- }
-
- builder.addln(";");
-
- builder.addln("return;");
+ builder.addln("case %d: %s%d = %s; return;",
+ i, PARAMETER_FIELD, i,
+ ClassFabUtils.castReference("$2", typeName));
}
builder.addln("default: throw new IllegalArgumentException(\"Parameter index out of range.\");");
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/AspectDecorator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/AspectDecorator.java?rev=651797&r1=651796&r2=651797&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/AspectDecorator.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry/ioc/services/AspectDecorator.java Fri Apr 25 20:00:55 2008
@@ -19,7 +19,8 @@
/**
* A decorator used to create an interceptor that delegates each method's invocation to an {@link
- * org.apache.tapestry.ioc.MethodAdvice} for advise.
+ * org.apache.tapestry.ioc.MethodAdvice} for advice. Advice can inspect or change method parameters, inspect or change
+ * the method's return value, and inspect and change thrown exceptions (checked and unchecked).
*/
public interface AspectDecorator
{
@@ -37,7 +38,7 @@
<T> T build(Class<T> serviceInterface, T delegate, MethodAdvice advice, String description);
/**
- * Creates a builder that can be used to create the builder. This is used when only some of the methods of need to
+ * Creates a builder that can be used to create the interceptor. This is used when only some of the methods need to
* be advised, or when different methods need to recieve different advice.
*
* @param serviceInterface defines the interface of the interceptor and the delegate