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/06/23 20:05:37 UTC

svn commit: r670691 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/internal/structure/ main/java/org/apache/tapestry5...

Author: hlship
Date: Mon Jun 23 11:05:36 2008
New Revision: 670691

URL: http://svn.apache.org/viewvc?rev=670691&view=rev
Log:
TAPESTRY-2471: Parameter fields that are updated during component event processing (such as a form submit) may not clear their values at the end of the request
TAPESTRY-2460: Nested BeanEditors (where the block parameter for a property to one BeanEditor contains another BeanEditor) results in a StackOverflowException

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanDisplay.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanEditor.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/data/Person.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanDisplay.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanEditor.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanDisplay.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/PropertyEditor.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Start.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/resources/log4j.properties

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanDisplay.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanDisplay.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanDisplay.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanDisplay.java Mon Jun 23 11:05:36 2008
@@ -96,6 +96,13 @@
     @Property(write = false)
     private ComponentResources overrides;
 
+    /**
+     * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}.
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL)
+    private String add;
+
+
     @Inject
     private ComponentDefaultProvider defaultProvider;
 
@@ -121,7 +128,7 @@
         if (model == null) model = modelSource.create(object.getClass(), false, overrides
                 .getContainerResources());
 
-        BeanModelUtils.modify(model, null, include, exclude, reorder);
+        BeanModelUtils.modify(model, add, include, exclude, reorder);
     }
 
     /**

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.java Mon Jun 23 11:05:36 2008
@@ -29,17 +29,19 @@
  * A component that creates an entire form editing the properties of a particular bean. Inspired by <a
  * href="http://www.trailsframework.org/">Trails</a> and <a href="http://beanform.sourceforge.net/">BeanForm</a> (both
  * for Tapestry 4). Generates a simple UI for editing the properties of a JavaBean, with the flavor of UI for each
- * property (text field, checkbox, drop down list) determined from the property type, and the order and validation for
- * the properties determined from annotations on the property's getter and setter methods.
+ * property (text field, checkbox, drop down list) determined from the property type (or by other means, such as an
+ * annotation), and the order and validation for the properties determined from annotations on the property's getter and
+ * setter methods.
  * <p/>
  * You may add &lt;t:parameter&gt;s to the component; when the name matches (case insensitive) the name of a property,
  * then the corresponding Block is renderered, rather than any of the built in property editor blocks. This allows you
  * to override specific properties with your own customized UI, for cases where the default UI is insufficient, or no
  * built-in editor type is appropriate.
  *
- * @see BeanModel
- * @see BeanModelSource
- * @see PropertyEditor
+ * @see org.apache.tapestry5.beaneditor.BeanModel
+ * @see org.apache.tapestry5.services.BeanModelSource
+ * @see org.apache.tapestry5.corelib.components.PropertyEditor
+ * @see org.apache.tapestry5.beaneditor.DataType
  */
 @SupportsInformalParameters
 public class BeanEditForm implements ClientElement, FormValidationControl
@@ -72,6 +74,12 @@
     private String include;
 
     /**
+     * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}.
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL)
+    private String add;
+
+    /**
      * A comma-separated list of property names to be removed from the {@link org.apache.tapestry5.beaneditor.BeanModel}.
      * The names are case-insensitive.
      */
@@ -142,7 +150,7 @@
             model = beanModelSource.create(beanType, true, resources.getContainerResources());
         }
 
-        BeanModelUtils.modify(model, null, include, exclude, reorder);
+        BeanModelUtils.modify(model, add, include, exclude, reorder);
     }
 
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditor.java Mon Jun 23 11:05:36 2008
@@ -67,7 +67,6 @@
      * Only these properties will be retained, and the properties will also be reordered. The names are
      * case-insensitive.
      */
-    @SuppressWarnings("unused")
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
     private String include;
 
@@ -87,6 +86,13 @@
     private String reorder;
 
     /**
+     * A comma-separated list of property names to be added to the {@link org.apache.tapestry5.beaneditor.BeanModel}.
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL)
+    private String add;
+
+
+    /**
      * The model that identifies the parameters to be edited, their order, and every other aspect. If not specified, a
      * default bean model will be created from the type of the object bound to the object parameter.
      */
@@ -149,7 +155,7 @@
             model = modelSource.create(type, true, overrides.getContainerResources());
         }
 
-        BeanModelUtils.modify(model, null, include, exclude, reorder);
+        BeanModelUtils.modify(model, add, include, exclude, reorder);
 
         // The only problem here is that if the bound property is backed by a persistent field, it
         // is assigned (and stored to the session, and propagated around the cluster) first,

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/PropertyEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/PropertyEditor.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/PropertyEditor.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/PropertyEditor.java Mon Jun 23 11:05:36 2008
@@ -67,8 +67,16 @@
         {
             component.cleanupEnvironment();
         }
+
+        @Override
+        public String toString()
+        {
+            return "PropertyEditor.CleanupEnvironment";
+        }
     }
 
+    private static final ComponentAction CLEANUP_ENVIRONMENT = new CleanupEnvironment();
+
     /**
      * The object to be edited by the BeanEditor. This will be read when the component renders and updated when the form
      * for the component is submitted. Typically, the container will listen for a "prepare" event, in order to ensure
@@ -213,7 +221,7 @@
         // Removes the PropertyEditContext after this component (including the editor block)
         // has rendered.
 
-        formSupport.storeAndExecute(this, new CleanupEnvironment());
+        formSupport.storeAndExecute(this, CLEANUP_ENVIRONMENT);
     }
 
     /**
@@ -231,8 +239,14 @@
 
         String dataType = propertyModel.getDataType();
 
+        if (dataType == null)
+            throw new RuntimeException(
+                    String.format("The data type for property '%s' of %s is null.", propertyModel.getPropertyName(),
+                                  object));
+
         try
         {
+
             return beanBlockSource.getEditBlock(dataType);
         }
         catch (RuntimeException ex)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventImpl.java Mon Jun 23 11:05:36 2008
@@ -18,6 +18,7 @@
 import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.internal.structure.PageResources;
 import org.apache.tapestry5.runtime.ComponentEvent;
+import org.slf4j.Logger;
 
 public class ComponentEventImpl extends EventImpl implements ComponentEvent
 {
@@ -31,16 +32,16 @@
 
     /**
      * @param eventType              non blank string used to identify the type of event that was triggered
-     * @param originatingComponentId the id of the component that triggered the event (this will likely need to change
-     *                               somewhat)
+     * @param originatingComponentId the id of the component that triggered the event
      * @param context                provides access to parameter values
      * @param handler                invoked when a non-null return value is obtained from an event handler method
      * @param pageResources          provides access to common resources and services
+     * @param logger                 used to log method invocations
      */
     public ComponentEventImpl(String eventType, String originatingComponentId, EventContext context,
-                              ComponentEventCallback handler, PageResources pageResources)
+                              ComponentEventCallback handler, PageResources pageResources, Logger logger)
     {
-        super(handler);
+        super(handler, logger);
 
         this.eventType = eventType;
         this.originatingComponentId = originatingComponentId;
@@ -48,6 +49,14 @@
         this.context = context;
     }
 
+
+    @Override
+    public String toString()
+    {
+        return String.format("ComponentEvent[%s from %s]", eventType,
+                             originatingComponentId.length() == 0 ? "(self)" : originatingComponentId);
+    }
+
     public boolean matches(String eventType, String componentId, int parameterCount)
     {
         return this.eventType.equalsIgnoreCase(

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java Mon Jun 23 11:05:36 2008
@@ -17,6 +17,7 @@
 import org.apache.tapestry5.ComponentEventCallback;
 import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry5.runtime.Event;
+import org.slf4j.Logger;
 
 public class EventImpl implements Event
 {
@@ -26,9 +27,16 @@
 
     private final ComponentEventCallback handler;
 
-    public EventImpl(ComponentEventCallback handler)
+    private final Logger logger;
+
+    /**
+     * @param handler informed of return values from methods, deems when the event is aborted
+     * @param logger  used to log method invocations
+     */
+    public EventImpl(ComponentEventCallback handler, Logger logger)
     {
         this.handler = notNull(handler, "handler");
+        this.logger = logger;
     }
 
     public boolean isAborted()
@@ -38,6 +46,9 @@
 
     public void setMethodDescription(String methodDescription)
     {
+        if (logger.isDebugEnabled())
+            logger.debug("Invoking: " + methodDescription);
+
         this.methodDescription = methodDescription;
     }
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Mon Jun 23 11:05:36 2008
@@ -176,7 +176,7 @@
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -205,7 +205,7 @@
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -234,7 +234,7 @@
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -263,7 +263,7 @@
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -294,7 +294,7 @@
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
             final RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -325,7 +325,7 @@
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -365,12 +365,14 @@
 
     private final PageResources pageResources;
 
+    private final Logger logger;
+
     private final RenderCommand cleanupRender = new RenderCommand()
     {
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -466,7 +468,7 @@
             elementAtSetup = writer.getElement();
 
             RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler);
+            final Event event = new EventImpl(handler, logger);
 
             ComponentCallback callback = new ComponentCallback()
             {
@@ -570,6 +572,8 @@
                                                            completeId, nestedId, instantiator);
 
         coreComponent = coreResources.getComponent();
+
+        logger = coreResources.getLogger();
     }
 
     /**
@@ -1019,8 +1023,13 @@
         {
             try
             {
+                Logger logger = component.getLogger();
+
                 ComponentEvent event = new ComponentEventImpl(currentEventType, componentId, currentContext, wrapped,
-                                                              pageResources);
+                                                              pageResources, logger);
+
+                if (logger.isDebugEnabled())
+                    logger.debug("Dispatch event: " + event);
 
                 result |= component.dispatchEvent(event);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java Mon Jun 23 11:05:36 2008
@@ -111,8 +111,7 @@
 
         String invariantFieldName = transformation.addField(Modifier.PRIVATE, "boolean", fieldName + "_invariant");
 
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
+        BodyBuilder builder = new BodyBuilder().begin();
 
         addDefaultBindingSetup(parameterName, defaultPrefix, defaultBinding, resourcesFieldName, transformation,
                                builder);
@@ -143,8 +142,15 @@
             builder.addln("%s = false;", cachedFieldName);
             builder.end();
 
-            transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, builder
-                    .toString());
+            // Clean up after the component renders.
+
+            String body = builder.toString();
+
+            transformation.extendMethod(TransformConstants.POST_RENDER_CLEANUP_SIGNATURE, body);
+
+            // And again, when the page is detached (TAPESTRY-2460)
+
+            transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, builder.toString());
         }
 
         return invariantFieldName;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/BeanEditForm.tml Mon Jun 23 11:05:36 2008
@@ -4,8 +4,7 @@
 
     <div class="t-beaneditor">
 
-        <t:beaneditor t:id="editor" object="object" remove="inherit:remove"
-                      reorder="inherit:reorder" model="model" overrides="this"/>
+        <t:beaneditor t:id="editor" object="object" model="model" overrides="this"/>
 
         <div class="t-beaneditor-row">
             <input type="submit" class="t-beaneditor-submit" value="${submitLabel}"/>

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanDisplay.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanDisplay.tml?rev=670691&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanDisplay.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanDisplay.tml Mon Jun 23 11:05:36 2008
@@ -0,0 +1,15 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+    <h1>Nested BeanDisplay</h1>
+
+    <p>Demonstrates a BeanEditor as the property editor inside a BeanEditForm.</p>
+
+    <t:beandisplay object="parent" add="child">
+
+        <t:parameter name="child">
+            <t:beandisplay object="parent.child"/>
+        </t:parameter>
+
+    </t:beandisplay>
+
+</html>

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanEditor.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanEditor.tml?rev=670691&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanEditor.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/NestedBeanEditor.tml Mon Jun 23 11:05:36 2008
@@ -0,0 +1,19 @@
+<html t:type="Border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+
+    <h1>Nested BeanEditor</h1>
+
+    <p>Demonstrates a BeanEditor as the property editor inside a BeanEditForm.</p>
+
+    <t:beaneditform object="parent" add="child">
+
+        <t:parameter name="child">
+
+            <div class="t-beaneditor" style="margin-left: 45px;">
+                <h2>Child</h2>
+                <t:beaneditor object="parent.child"/>
+            </div>
+        </t:parameter>
+
+    </t:beaneditform>
+
+</html>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java Mon Jun 23 11:05:36 2008
@@ -1823,7 +1823,7 @@
 
         assertText("activePageName", "music/Details");
     }
-    
+
     /**
      * TAPESTRY-2235
      */
@@ -1837,9 +1837,9 @@
         clickAndWait("link=Wake Me Up (Copy)");
 
         assertText("activePageName", "music/Details2");
-        
+
         assertText("//div[@class='t-beandisplay-value title']", "Wake Me Up");
-        
+
         assertText("//div[@class='t-beandisplay-value artist']", "Norah Jones");
     }
 
@@ -2058,4 +2058,25 @@
 
         assertText("output-text", "yrtsepaT");
     }
+
+    // TAPESTRY-2460
+
+    @Test
+    public void nested_bean_editor_and_bean_display()
+    {
+        start("Nested BeanEditor");
+
+        type("name", "Parent");
+        type("age", "60");
+
+        type("name_0", "Child");
+        type("age_0", "40");
+
+        clickAndWait(SUBMIT);
+
+        assertText("//div[@id='content']//h1", "Nested BeanDisplay");
+
+        // As usual, Selenium is fighting me in terms of extracting data, so the above check just ensures
+        // we made it past the form submit without error.
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/data/Person.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/data/Person.java?rev=670691&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/data/Person.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/data/Person.java Mon Jun 23 11:05:36 2008
@@ -0,0 +1,58 @@
+// 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.tapestry5.integration.app1.data;
+
+import org.apache.tapestry5.beaneditor.Validate;
+
+public class Person
+{
+    @Validate("required,minlength=5")
+    private String name;
+
+    @Validate("required,min=1")
+    private int age;
+
+    private Person child;
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public int getAge()
+    {
+        return age;
+    }
+
+    public void setAge(int age)
+    {
+        this.age = age;
+    }
+
+    public Person getChild()
+    {
+        return child;
+    }
+
+    public void setChild(Person child)
+    {
+        this.child = child;
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanDisplay.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanDisplay.java?rev=670691&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanDisplay.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanDisplay.java Mon Jun 23 11:05:36 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.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.app1.data.Person;
+
+public class NestedBeanDisplay
+{
+    @Persist
+    @Property
+    private Person parent;
+
+    Object initialize(Person person)
+    {
+        parent = person;
+
+        return this;
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanEditor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanEditor.java?rev=670691&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanEditor.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/NestedBeanEditor.java Mon Jun 23 11:05:36 2008
@@ -0,0 +1,39 @@
+// 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.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.InjectPage;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.app1.data.Person;
+
+public class NestedBeanEditor
+{
+    @Property
+    private Person parent;
+
+    @InjectPage
+    private NestedBeanDisplay display;
+
+    void onPrepare()
+    {
+        parent = new Person();
+        parent.setChild(new Person());
+    }
+
+    Object onSuccess()
+    {
+        return display.initialize(parent);
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Start.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Start.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Start.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Start.java Mon Jun 23 11:05:36 2008
@@ -61,6 +61,10 @@
     }
 
     private static final List<Item> ITEMS = CollectionFactory.newList(
+
+            new Item("nestedbeaneditor", "Nested BeanEditor",
+                     "BeanEditor as override for property editor in BeanEditForm"),
+
             new Item("actionpage", "Action Page", "tests fixture for ActionLink component"),
 
             new Item("cleancachedemo", "Clean Cache Demo", "cache cleared properly during Ajax calls"),

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java Mon Jun 23 11:05:36 2008
@@ -20,6 +20,7 @@
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.runtime.ComponentEvent;
+import org.slf4j.Logger;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -50,7 +51,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
 
         assertTrue(event.matches("eventType", "someId", 0));
         assertFalse(event.matches("foo", "someId", 0));
@@ -68,7 +69,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
 
         assertTrue(event.matches("EVENTTYPE", "someid", 0));
 
@@ -85,7 +86,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
 
         assertTrue(event.matches("eventType", "someId", 0));
 
@@ -104,7 +105,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
 
         assertTrue(event.matches("eventtype", "SOMEID", 0));
 
@@ -126,7 +127,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, null);
 
         assertSame(event.coerceContext(0, "java.lang.Integer"), value);
 
@@ -138,12 +139,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         event.setMethodDescription("foo.Bar.baz()");
 
@@ -166,6 +170,9 @@
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
         PageResources resources = mockPageResources();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_toClass(resources, Integer.class.getName(), Integer.class);
 
@@ -175,7 +182,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, logger);
 
         event.setMethodDescription("foo.Bar.baz()");
 
@@ -200,6 +207,9 @@
     {
         Object result = new Object();
         String methodDescription = "foo.Bar.baz()";
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         ComponentEventCallback handler = mockComponentEventHandler();
 
@@ -207,7 +217,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null, logger);
 
         event.setMethodDescription(methodDescription);
 
@@ -226,12 +236,15 @@
         Object result = new Object();
         String methodDescription = "foo.Bar.baz()";
         ComponentEventCallback handler = mockComponentEventHandler();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_handleResult(handler, result, false);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null, logger);
 
         event.setMethodDescription(methodDescription);
 
@@ -246,10 +259,13 @@
     public void store_null_result_does_not_abort_or_invoke_handler()
     {
         ComponentEventCallback handler = mockComponentEventHandler();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null, logger);
 
         event.setMethodDescription("foo.Bar.baz()");
 
@@ -266,12 +282,15 @@
     {
         Object result = new Object();
         ComponentEventCallback handler = mockComponentEventHandler();
+        Logger logger = mockLogger();
+
+        expect(logger.isDebugEnabled()).andStubReturn(false);
 
         expect(handler.handleResult(result)).andReturn(true);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", null, handler, null, logger);
 
         event.setMethodDescription("foo.Bar.baz()");
         event.storeResult(result);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java Mon Jun 23 11:05:36 2008
@@ -28,6 +28,7 @@
 import org.apache.tapestry5.model.ParameterModel;
 import org.apache.tapestry5.runtime.Component;
 import org.easymock.EasyMock;
+import org.slf4j.Logger;
 import org.testng.annotations.Test;
 
 public class ComponentPageElementImplTest extends InternalBaseTestCase
@@ -50,11 +51,15 @@
         Component component = mockComponent();
         ComponentModel model = mockComponentModel();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
         replay();
 
+
         ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, null);
 
         ComponentResources resources = cpe.getComponentResources();
@@ -80,9 +85,12 @@
         ComponentModel model = mockComponentModel();
         TypeCoercer coercer = mockTypeCoercer();
         Block block = mockBlock();
+        Logger logger = mockLogger();
 
         Instantiator ins = newInstantiator(component, model);
 
+        train_getLogger(model, logger);
+
         replay();
 
         ComponentPageElement cpe = new ComponentPageElementImpl(page, ins, null);
@@ -106,9 +114,12 @@
         ComponentModel model = mockComponentModel();
         Binding binding = mockBinding();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
 
         Instantiator ins = newInstantiator(component, model);
 
+        train_getLogger(model, logger);
+
         train_getParameterModel(model, "barney", null);
 
         train_getSupportsInformalParameters(model, true);
@@ -137,6 +148,9 @@
         TypeCoercer coercer = mockTypeCoercer();
         Block block1 = mockBlock();
         Block block2 = mockBlock();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -169,6 +183,9 @@
         Binding binding = mockBinding();
         TypeCoercer coercer = mockTypeCoercer();
         ParameterModel pmodel = mockParameterModel();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -196,6 +213,9 @@
         ComponentModel model = mockComponentModel();
         ParameterModel pmodel = mockParameterModel();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -225,6 +245,9 @@
         ParameterModel pmodel = mockParameterModel();
         Location l = mockLocation();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -276,6 +299,9 @@
         Binding binding = mockBinding();
         TypeCoercer coercer = mockTypeCoercer();
         ParameterModel pmodel = mockParameterModel();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -305,6 +331,9 @@
         ComponentModel model = mockComponentModel();
         Binding binding = mockBinding();
         PageResources resources = mockPageResources();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         train_getSupportsInformalParameters(model, true);
 
@@ -338,6 +367,9 @@
         ComponentModel model = mockComponentModel();
         PageResources resources = mockPageResources();
         Binding binding = mockBinding();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -369,6 +401,9 @@
         Component component = mockComponent();
         ComponentModel model = mockComponentModel();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
         Instantiator ins2 = newInstantiator(component, model);
@@ -401,6 +436,9 @@
         ComponentPageElement childElement = mockComponentPageElement();
         Component childComponent = mockComponent();
         TypeCoercer coercer = mockTypeCoercer();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
 
@@ -432,6 +470,9 @@
         ComponentPageElement child2 = mockComponentPageElement();
         TypeCoercer coercer = mockTypeCoercer();
         Location l = mockLocation();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(pageComponent, model);
 
@@ -470,6 +511,9 @@
         final String mixinClassName = "foo.Bar";
         Component mixin = mockComponent();
         ComponentModel mixinModel = mockComponentModel();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
         Instantiator mixinIns = newInstantiator(mixin, mixinModel);
@@ -496,6 +540,9 @@
         TypeCoercer coercer = mockTypeCoercer();
         Component mixin = mockComponent();
         ComponentModel mixinModel = mockComponentModel();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
         Instantiator mixinIns = newInstantiator(mixin, mixinModel);
@@ -532,6 +579,9 @@
         Component mixin = mockComponent();
         TypeCoercer coercer = mockTypeCoercer();
         Binding binding = mockBinding();
+        Logger logger = mockLogger();
+
+        train_getLogger(model, logger);
 
         Instantiator ins = newInstantiator(component, model);
         Instantiator mixinInstantiator = newInstantiator(mixin, mixinModel);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/log4j.properties?rev=670691&r1=670690&r2=670691&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/log4j.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/log4j.properties Mon Jun 23 11:05:36 2008
@@ -23,8 +23,5 @@
 
 log4j.category.org.apache.tapestry5.integration.app1=debug
 
-log4j.category.org.apache.tapestry5.corelib.components.AjaxFormLoop=debug
-log4j.category.org.apache.tapestry5.corelib.components.Form=debug
-log4j.category.org.apache.tapestry5.corelib.components.FormFragment=debug
-log4j.category.org.apache.tapestry5.corelib.components.FormInjector=debug
+# log4j.category.org.apache.tapestry5.corelib.components=debug