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