You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2007/02/19 01:04:26 UTC

svn commit: r509035 [1/2] - in /tapestry/tapestry4/trunk: ./ tapestry-annotations/src/descriptor/META-INF/ tapestry-annotations/src/java/org/apache/tapestry/annotations/ tapestry-annotations/src/test/org/apache/tapestry/annotations/ tapestry-examples/T...

Author: jkuhnert
Date: Sun Feb 18 16:04:24 2007
New Revision: 509035

URL: http://svn.apache.org/viewvc?view=rev&rev=509035
Log:
Fixed annoyance with persistent property state changes not being detected unless you explicitly set them on a 
page property. Only goes one level deep but now uses cglib enhancements to detect property ~object~ changes as well.
(plus some additional rules to make sure it doesn't conflict with existing bytecode enhancement libraries like 
hibernate / javax.persistence / etc .. )

Added:
    tapestry/tapestry4/trunk/tapestry-annotations/src/java/org/apache/tapestry/annotations/ComponentPropertyProxyWorker.java
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/Entity.java
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SimpleBean.java
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SubSimpleBean.java
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/TestComponentPropertyProxyWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectChangeObserverWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibPropertyChangeInterceptor.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibProxiedPropertyChangeObserverImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/LazyProxyDelegate.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/ObservableMethodFilter.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/PropertyChangeObserver.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/record/SimpleState.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/record/TestPropertyChangeObserver.java
Modified:
    tapestry/tapestry4/trunk/pom.xml
    tapestry/tapestry4/trunk/tapestry-annotations/src/descriptor/META-INF/hivemodule.xml
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/AnnotatedPage.java
    tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/BasicComponent.java
    tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/TaskEntryPage.java
    tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/Redirect.html
    tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/localization/Change.html
    tapestry/tapestry4/trunk/tapestry-framework/pom.xml
    tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.enhance.xml
    tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.persist.xml
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/AbstractPage.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IPage.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/IPageRecorder.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/DispatchToInjectWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectRenderWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ChangeObserver.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ObservedChangeEvent.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/PageRecorderImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/PersistentPropertyData.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/SessionPropertyPersistenceStrategy.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/RequestLocaleManagerImpl.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/spec/IPropertySpecification.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/spec/PropertySpecification.java
    tapestry/tapestry4/trunk/tapestry-framework/src/scripts/TestDefaultParameterValues.xml
    tapestry/tapestry4/trunk/tapestry-framework/src/test-data/context27/Home.html
    tapestry/tapestry4/trunk/tapestry-framework/src/test-data/context27/WEB-INF/Home.page
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/enhance/TestSpecifiedPropertyWorker.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/junit/mock/TestMockApplications.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/record/PageFixture.java

Modified: tapestry/tapestry4/trunk/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/pom.xml?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/pom.xml (original)
+++ tapestry/tapestry4/trunk/pom.xml Sun Feb 18 16:04:24 2007
@@ -123,6 +123,12 @@
                 <version>1.1.1</version>
                 <scope>compile</scope>
             </dependency>
+            <dependency>
+                <groupId>cglib</groupId>
+                <artifactId>cglib-nodep</artifactId>
+                <version>2.1_3</version>
+                <scope>compile</scope>
+            </dependency>
             <!-- Really, a transitive dependency of hivemind. -->
             <dependency>
                 <groupId>oro</groupId>
@@ -270,7 +276,7 @@
                         </systemProperties>
                         <parallel>true</parallel>
                         <threadCount>4</threadCount>
-			<excludedGroups>integration</excludedGroups>
+                        <excludedGroups>integration</excludedGroups>
                     </configuration>
                 </plugin>
                 <plugin>
@@ -297,6 +303,12 @@
                     </executions>
                 </plugin>
                 <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-source-plugin</artifactId>
+                    <version>2.0.2-SNAPSHOT</version>
+                    <inherited>true</inherited>
+                </plugin>
+               <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-dependency-plugin</artifactId>
                     <version>2.0-alpha-1-SNAPSHOT</version>

Modified: tapestry/tapestry4/trunk/tapestry-annotations/src/descriptor/META-INF/hivemodule.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/descriptor/META-INF/hivemodule.xml?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/descriptor/META-INF/hivemodule.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/descriptor/META-INF/hivemodule.xml Sun Feb 18 16:04:24 2007
@@ -1,205 +1,241 @@
 <?xml version="1.0"?>
 <!-- 
-   Copyright 2005 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.
+    Copyright 2005 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.
 -->
 
 <module id="tapestry.annotation" version="4.0.0" package="org.apache.tapestry.annotations">
-  
-  JDK 1.5 annotation support for Tapestry, allowing classes (including base classes) to provide details normally
-  specified in the XML component or page specification.
-  
-  <service-point id="AnnotationEnhancementWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
-    
-    Bridge from ordinary specification-based enhancements, to annotation-driven enhancements.
-    
-    <invoke-factory>
-      <construct class="AnnotationEnhancementWorker">
-        <set-configuration property="methodWorkers" configuration-id="MethodWorkers"/>
-        <set-configuration property="classWorkers" configuration-id="ClassWorkers"/>
-        <set-configuration property="secondaryAnnotationWorkers" configuration-id="SecondaryAnnotationWorkers"/>
-      </construct>
-    </invoke-factory>
-    
-  </service-point>
-  
-  <service-point id="ComponentHousekeepingWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
-  
-  	Perform additional housekeeping relating to injected components, i.e. ensure bindings are set
-  	for copyOf components.
-  
-  	<invoke-factory>
-      <construct class="ComponentHousekeepingWorker">
-      </construct>
-    </invoke-factory>
-  </service-point>
-  
-  <!-- Only contribute when in JDK 1.5 and java.lang.Annotation exists -->
-  
-  <contribution configuration-id="tapestry.enhance.EnhancementWorkers" if="class java.lang.annotation.Annotation">    
-    <!-- Needs to come first because some enhancements modify
-         the component specification, affecting what occurs in later commands. -->
-    <command id="annotation-worker" object="service:AnnotationEnhancementWorker" before="*"/>
-    <command id="component-housekeeping-worker" object="service:ComponentHousekeepingWorker" after="annotation-worker"/>
-    <command id="inject-asset-annotation" object="service:InjectAssetAnnotationWorker" after="tapestry.enhance.inject-asset"/>
-  </contribution>
-  
-  <configuration-point id="ClassWorkers">
-    
-    Maps from a specific Annotation class, to a particular ClassAnnotationEnhancementWorker (typically,
-    a service).
-    <schema>
-      <element name="worker" key-attribute="annotation">
-        
-        <attribute name="annotation" translator="class">
-          The name of an Annotation class which triggers the enhancement worker's behavior.
-        </attribute>
-        
-        <attribute name="object" translator="object">
-          
-          An object that implements the ClassAnnotationEnhancementWorker interface.
-        </attribute>
-        
-        <rules>
-          <push-attribute attribute="object"/>
-          <invoke-parent method="addElement"/>
-        </rules>
-        
-      </element>
-    </schema>
-    
-  </configuration-point>
-  
-  <contribution configuration-id="ClassWorkers">
-    <worker annotation="ComponentClass" object="instance:ComponentClassAnnotationWorker"/>
-    <worker annotation="Meta" object="instance:MetaAnnotationWorker"/>
-  </contribution>
-  
-  <configuration-point id="MethodWorkers">
-    
-    Maps from a specific Annotation class, to a particular MethodAnnotationEnhancementWorker (typically,
-    a service).
-    <schema>
-      <element name="worker" key-attribute="annotation">
-        
-        <attribute name="annotation" translator="class">
-          The name of an Annotation class which triggers the enhancement worker's behavior.
-        </attribute>
-        
-        <attribute name="object" translator="object">
-          
-          An object that implements the MethodAnnotationEnhancementWorker interface.
-        </attribute>
-        
-        <rules>
-          <push-attribute attribute="object"/>
-          <invoke-parent method="addElement"/>
-        </rules>
-        
-      </element>
-    </schema>
-    
-  </configuration-point>
-  
-  <contribution configuration-id="MethodWorkers">
-    
-    <!-- These instantiate the corresponding enhancement worker, and delegate to it. -->
-    <worker annotation="InjectObject" object="service:InjectObjectAnnotationWorker"/>
-    <worker annotation="InjectComponent" object="instance:InjectComponentAnnotationWorker"/>
-    <worker annotation="Message" object="instance:MessageAnnotationWorker"/>
-    
-    <!-- These update the component specification with new sub-elements. Later, the 
-         existing enhancement workers perform the work, as if the sub-elements were
-         specified in the XML. This is actually a lot easier and just as powerful, and
-         the delegating style will probably go pretty soon.  -->
-         
-    <worker annotation="InjectState" object="instance:InjectStateAnnotationWorker"/>
-    <worker annotation="InjectStateFlag" object="instance:InjectStateFlagAnnotationWorker"/>
-    <worker annotation="Persist" object="instance:PersistAnnotationWorker"/>
-    <worker annotation="Bean" object="instance:BeanAnnotationWorker"/>
-    <worker annotation="Asset" object="instance:AssetAnnotationWorker"/>
-    <worker annotation="Parameter" object="instance:ParameterAnnotationWorker"/>
-    <worker annotation="InjectPage" object="instance:InjectPageAnnotationWorker"/>
-    <worker annotation="Component" object="service:ComponentAnnotationWorker"/>
-    <worker annotation="InjectMeta" object="instance:InjectMetaAnnotationWorker"/>
-    <worker annotation="InjectScript" object="instance:InjectScriptAnnotationWorker"/>
-  </contribution>
-  
-  <service-point id="ComponentAnnotationWorker" interface="MethodAnnotationEnhancementWorker">
-    <invoke-factory>
-      <construct class="ComponentAnnotationWorker" />
-    </invoke-factory>
-  </service-point>
-  
-  <service-point id="InjectAssetAnnotationWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
-    
-    Injects IAsset assets based on previously defined asset alias names. (such as in a .page or .jwc spec )
-    
-    <invoke-factory>
-      <construct class="InjectAssetAnnotationWorker"/>
-    </invoke-factory>
-    
-  </service-point>
-  
-  <service-point id="InjectObjectAnnotationWorker" interface="MethodAnnotationEnhancementWorker">
-    
-    Injects HiveMind objects, based on the InjectObject annotation.
-    
-    <invoke-factory>
-      <construct class="InjectObjectAnnotationWorker">
-        <set-service property="provider" service-id="tapestry.InjectedValueProvider"/>
-      </construct>
-    </invoke-factory>
-    
-  </service-point>
-  
-  <service-point id="EventListenerAnnotationWorker" interface="EventListenerAnnotationWorker">
-    
-    Handles EventListener annotations.
-    
-    <invoke-factory>
-      <construct class="EventListenerAnnotationWorker">
-        <set-service property="invoker" service-id="tapestry.event.EventInvoker"/>
-      </construct>
-    </invoke-factory>
-    
-  </service-point>
-  
-  <configuration-point id="SecondaryAnnotationWorkers">
-    
-    Configures a list of secondary annotation workers.
-    <schema>
-      <element name="worker">
-        
-        <attribute name="object" translator="object" required="true">
-          
-          An object that implements the SecondaryAnnotationEnhancementWorker interface.
-        </attribute>
-        
-        <rules>
-          <push-attribute attribute="object"/>
-          <invoke-parent method="addElement"/>
-        </rules>
+
+    JDK 1.5 annotation support for Tapestry, allowing classes (including base classes) to provide details normally
+    specified in the XML component or page specification.
+
+    <service-point id="AnnotationEnhancementWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+        Bridge from ordinary specification-based enhancements, to annotation-driven enhancements.
+
+        <invoke-factory>
+            <construct class="AnnotationEnhancementWorker">
+                <set-configuration property="methodWorkers" configuration-id="MethodWorkers" />
+                <set-configuration property="classWorkers" configuration-id="ClassWorkers" />
+                <set-configuration property="secondaryAnnotationWorkers" configuration-id="SecondaryAnnotationWorkers" />
+            </construct>
+        </invoke-factory>
+
+    </service-point>
+
+    <service-point id="ComponentHousekeepingWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+        Perform additional housekeeping relating to injected components, i.e. ensure bindings are set for copyOf
+        components.
+
+        <invoke-factory>
+            <construct class="ComponentHousekeepingWorker"></construct>
+        </invoke-factory>
+    </service-point>
+
+    <service-point id="ComponentPropertyProxyWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+        Checks for proxy capabilities of specified properties.
         
-      </element>
-    </schema>
+        <invoke-factory>
+            <construct class="ComponentPropertyProxyWorker">
+                <set-configuration property="excludedPackages" configuration-id="ExcludedProxyAnnotations" />
+            </construct>
+        </invoke-factory>
+    </service-point>
+
+    <!-- Only contribute when in JDK 1.5 and java.lang.Annotation exists -->
+
+    <contribution configuration-id="tapestry.enhance.EnhancementWorkers" if="class java.lang.annotation.Annotation">
+        <!-- Needs to come first because some enhancements modify
+            the component specification, affecting what occurs in later commands. -->
+        <command id="annotation-worker" object="service:AnnotationEnhancementWorker" before="*" />
+        
+        <command id="component-housekeeping-worker" object="service:ComponentHousekeepingWorker" after="annotation-worker" />
+        <command id="component-propertyproxy-worker" object="service:ComponentPropertyProxyWorker" before="tapestry.enhance.specified-property" />
+        <command id="inject-asset-annotation" object="service:InjectAssetAnnotationWorker" after="tapestry.enhance.inject-asset" />
+    </contribution>
+
+    <configuration-point id="ExcludedProxyAnnotations">
     
-  </configuration-point>
-  
-  <contribution configuration-id="SecondaryAnnotationWorkers"> 
-    <worker object="instance:InitialValueAnnotationWorker"/>
-    <worker object="service:EventListenerAnnotationWorker"/>
-  </contribution>
-  
+        Definition of simple string match configurations that will cause any annotation class name 
+        matching any of the definitions in this configuration point to not be eligable for cglib proxying
+        enhancements later on. This is done to prevent issues with popular persistence frameworks like
+        Hibernate.
+        
+        <schema>
+            <element name="exclude">
+                <attribute name="match" required="true" unique="true" >
+                    The string to use in matching containment within the string returned from Annotation.annotationType().getName().
+                </attribute>
+                <rules>
+                    <push-attribute attribute="match" />
+                    <invoke-parent method="addElement" />
+                </rules>
+            </element>
+        </schema>
+    </configuration-point>
+
+    <contribution configuration-id="ExcludedProxyAnnotations">
+        <exclude match="org.hibernate" />
+        <exclude match="javax.persistence" />
+        <exclude match="Entity" />
+    </contribution>
+
+    <configuration-point id="ClassWorkers">
+
+        Maps from a specific Annotation class, to a particular ClassAnnotationEnhancementWorker (typically, a service).
+        <schema>
+            <element name="worker" key-attribute="annotation">
+
+                <attribute name="annotation" translator="class">
+                    The name of an Annotation class which triggers the enhancement worker's behavior.
+                </attribute>
+
+                <attribute name="object" translator="object">
+
+                    An object that implements the ClassAnnotationEnhancementWorker interface.
+                </attribute>
+
+                <rules>
+                    <push-attribute attribute="object" />
+                    <invoke-parent method="addElement" />
+                </rules>
+
+            </element>
+        </schema>
+
+    </configuration-point>
+
+    <contribution configuration-id="ClassWorkers">
+        <worker annotation="ComponentClass" object="instance:ComponentClassAnnotationWorker" />
+        <worker annotation="Meta" object="instance:MetaAnnotationWorker" />
+    </contribution>
+
+    <configuration-point id="MethodWorkers">
+
+        Maps from a specific Annotation class, to a particular MethodAnnotationEnhancementWorker (typically, a service).
+        <schema>
+            <element name="worker" key-attribute="annotation">
+
+                <attribute name="annotation" translator="class">
+                    The name of an Annotation class which triggers the enhancement worker's behavior.
+                </attribute>
+
+                <attribute name="object" translator="object">
+
+                    An object that implements the MethodAnnotationEnhancementWorker interface.
+                </attribute>
+
+                <rules>
+                    <push-attribute attribute="object" />
+                    <invoke-parent method="addElement" />
+                </rules>
+
+            </element>
+        </schema>
+
+    </configuration-point>
+
+    <contribution configuration-id="MethodWorkers">
+
+        <!-- These instantiate the corresponding enhancement worker, and delegate to it. -->
+        <worker annotation="InjectObject" object="service:InjectObjectAnnotationWorker" />
+        <worker annotation="InjectComponent" object="instance:InjectComponentAnnotationWorker" />
+        <worker annotation="Message" object="instance:MessageAnnotationWorker" />
+
+        <!-- These update the component specification with new sub-elements. Later, the 
+            existing enhancement workers perform the work, as if the sub-elements were
+            specified in the XML. This is actually a lot easier and just as powerful, and
+            the delegating style will probably go pretty soon.  -->
+
+        <worker annotation="InjectState" object="instance:InjectStateAnnotationWorker" />
+        <worker annotation="InjectStateFlag" object="instance:InjectStateFlagAnnotationWorker" />
+        <worker annotation="Persist" object="instance:PersistAnnotationWorker" />
+        <worker annotation="Bean" object="instance:BeanAnnotationWorker" />
+        <worker annotation="Asset" object="instance:AssetAnnotationWorker" />
+        <worker annotation="Parameter" object="instance:ParameterAnnotationWorker" />
+        <worker annotation="InjectPage" object="instance:InjectPageAnnotationWorker" />
+        <worker annotation="Component" object="service:ComponentAnnotationWorker" />
+        <worker annotation="InjectMeta" object="instance:InjectMetaAnnotationWorker" />
+        <worker annotation="InjectScript" object="instance:InjectScriptAnnotationWorker" />
+    </contribution>
+
+    <service-point id="ComponentAnnotationWorker" interface="MethodAnnotationEnhancementWorker">
+        <invoke-factory>
+            <construct class="ComponentAnnotationWorker" />
+        </invoke-factory>
+    </service-point>
+
+    <service-point id="InjectAssetAnnotationWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+        Injects IAsset assets based on previously defined asset alias names. (such as in a .page or .jwc spec )
+
+        <invoke-factory>
+            <construct class="InjectAssetAnnotationWorker" />
+        </invoke-factory>
+
+    </service-point>
+
+    <service-point id="InjectObjectAnnotationWorker" interface="MethodAnnotationEnhancementWorker">
+
+        Injects HiveMind objects, based on the InjectObject annotation.
+
+        <invoke-factory>
+            <construct class="InjectObjectAnnotationWorker">
+                <set-service property="provider" service-id="tapestry.InjectedValueProvider" />
+            </construct>
+        </invoke-factory>
+
+    </service-point>
+
+    <service-point id="EventListenerAnnotationWorker" interface="EventListenerAnnotationWorker">
+
+        Handles EventListener annotations.
+
+        <invoke-factory>
+            <construct class="EventListenerAnnotationWorker">
+                <set-service property="invoker" service-id="tapestry.event.EventInvoker" />
+            </construct>
+        </invoke-factory>
+
+    </service-point>
+
+    <configuration-point id="SecondaryAnnotationWorkers">
+
+        Configures a list of secondary annotation workers.
+        <schema>
+            <element name="worker">
+
+                <attribute name="object" translator="object" required="true">
+
+                    An object that implements the SecondaryAnnotationEnhancementWorker interface.
+                </attribute>
+
+                <rules>
+                    <push-attribute attribute="object" />
+                    <invoke-parent method="addElement" />
+                </rules>
+
+            </element>
+        </schema>
+
+    </configuration-point>
+
+    <contribution configuration-id="SecondaryAnnotationWorkers">
+        <worker object="instance:InitialValueAnnotationWorker" />
+        <worker object="service:EventListenerAnnotationWorker" />
+    </contribution>
+
 </module>

Added: tapestry/tapestry4/trunk/tapestry-annotations/src/java/org/apache/tapestry/annotations/ComponentPropertyProxyWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/java/org/apache/tapestry/annotations/ComponentPropertyProxyWorker.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/java/org/apache/tapestry/annotations/ComponentPropertyProxyWorker.java (added)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/java/org/apache/tapestry/annotations/ComponentPropertyProxyWorker.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,92 @@
+// Copyright 2004, 2005 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.annotations;
+
+import java.lang.annotation.Annotation;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tapestry.enhance.EnhanceUtils;
+import org.apache.tapestry.enhance.EnhancementOperation;
+import org.apache.tapestry.enhance.EnhancementWorker;
+import org.apache.tapestry.spec.IComponentSpecification;
+import org.apache.tapestry.spec.IPropertySpecification;
+
+
+/**
+ * Performs runtime checks on persistent properties to ensure that objects being
+ * managed by competing bytecode enhancement libraries (such as Hibernate) aren't
+ * proxied.
+ */
+public class ComponentPropertyProxyWorker implements EnhancementWorker
+{
+    
+    private List<String> _excludedPackages;
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
+    {
+        Iterator i = spec.getPropertySpecificationNames().iterator();
+
+        while(i.hasNext())
+        {
+            String name = (String) i.next();
+            IPropertySpecification ps = spec.getPropertySpecification(name);
+
+            checkProxy(op, ps);
+        }
+    }
+
+    void checkProxy(EnhancementOperation op, IPropertySpecification ps)
+    {
+        ps.setProxyChecked(true);
+        
+        if (!ps.isPersistent())
+            return;
+        
+        Class propertyType = EnhanceUtils.extractPropertyType(op, ps.getName(), ps.getType());
+        if (propertyType == null)
+            return;
+        
+        if (!EnhanceUtils.canProxyPropertyType(propertyType))
+            return;
+        
+        Annotation[] annotations = propertyType.getAnnotations();
+        
+        for (int i = 0; i < annotations.length; i++) {
+            if (isExcluded(annotations[i]))
+                return;
+        }
+        
+        ps.setCanProxy(true);
+    }
+    
+    boolean isExcluded(Annotation annotation)
+    {
+        for (String match : _excludedPackages) {
+            
+            if (annotation.annotationType().getName().indexOf(match) > -1)
+                return true;
+        }
+        
+        return false;
+    }
+    
+    public void setExcludedPackages(List packages)
+    {
+        _excludedPackages = packages;
+    }
+}

Modified: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/AnnotatedPage.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/AnnotatedPage.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/AnnotatedPage.java (original)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/AnnotatedPage.java Sun Feb 18 16:04:24 2007
@@ -165,4 +165,10 @@
     
     @InitialValue("literal:5")
     public abstract int getDefaultPageSize();
+    
+    @Persist
+    public abstract SimpleBean getBean();
+    
+    @Persist
+    public abstract SubSimpleBean getSubBean();
 }

Modified: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/BasicComponent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/BasicComponent.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/BasicComponent.java (original)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/BasicComponent.java Sun Feb 18 16:04:24 2007
@@ -23,5 +23,4 @@
 @ComponentClass
 public class BasicComponent
 {
-
 }

Added: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/Entity.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/Entity.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/Entity.java (added)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/Entity.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,34 @@
+// Copyright 2004, 2005 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.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines a basic annotation with a name matching that of most persistence frameworks.
+ */
+@Target( { ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface Entity
+{
+
+    
+}

Added: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SimpleBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SimpleBean.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SimpleBean.java (added)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SimpleBean.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,26 @@
+// Copyright 2004, 2005 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.annotations;
+
+
+/**
+ * Used to test proxy ignore rules.
+ */
+@Entity
+public class SimpleBean
+{
+    public SimpleBean()
+    {
+    }
+}

Added: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SubSimpleBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SubSimpleBean.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SubSimpleBean.java (added)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/SubSimpleBean.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,23 @@
+// Copyright 2004, 2005 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.annotations;
+
+
+/**
+ * Test of annotation inheritance.
+ */
+public class SubSimpleBean extends SimpleBean
+{
+
+}

Added: tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/TestComponentPropertyProxyWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/TestComponentPropertyProxyWorker.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/TestComponentPropertyProxyWorker.java (added)
+++ tapestry/tapestry4/trunk/tapestry-annotations/src/test/org/apache/tapestry/annotations/TestComponentPropertyProxyWorker.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,108 @@
+// Copyright 2004, 2005 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.annotations;
+
+import static org.easymock.EasyMock.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hivemind.Location;
+import org.apache.tapestry.enhance.EnhancementOperation;
+import org.apache.tapestry.spec.ComponentSpecification;
+import org.apache.tapestry.spec.IComponentSpecification;
+import org.apache.tapestry.spec.IPropertySpecification;
+import org.apache.tapestry.spec.PropertySpecification;
+import org.testng.annotations.Test;
+
+
+/**
+ * Tests functionality of {@link ComponentPropertyProxyWorker}.
+ */
+@Test
+public class TestComponentPropertyProxyWorker extends BaseAnnotationTestCase
+{
+    
+    void addProperty(EnhancementOperation op, IComponentSpecification spec, Location l, 
+            String propertyName, Class type)
+    {
+        IPropertySpecification pspec = new PropertySpecification();
+
+        pspec.setName(propertyName);
+        pspec.setPersistence("session");
+        pspec.setLocation(l);
+        pspec.setType(type.getName());
+        
+        spec.addPropertySpecification(pspec);
+        
+        expect(op.convertTypeName(type.getName())).andReturn(type);
+        op.validateProperty(propertyName, type);
+    }
+    
+    public void test_Excluded()
+    {
+        Location l = newLocation();
+        EnhancementOperation op = newOp();
+        IComponentSpecification spec = new ComponentSpecification();
+        
+        addProperty(op, spec, l, "bean", SimpleBean.class);
+        
+        ComponentPropertyProxyWorker worker = new ComponentPropertyProxyWorker();
+        
+        List exclude = new ArrayList();
+        exclude.add("Entity");
+        worker.setExcludedPackages(exclude);
+        
+        replay();
+        
+        worker.performEnhancement(op, spec);
+        
+        verify();
+        
+        IPropertySpecification prop = spec.getPropertySpecification("bean");
+        
+        assert prop != null;
+        assert prop.isPersistent();
+        assert prop.isProxyChecked();
+        assert !prop.canProxy();
+    }
+    
+    public void test_SubClass_Excluded()
+    {
+        Location l = newLocation();
+        EnhancementOperation op = newOp();
+        IComponentSpecification spec = new ComponentSpecification();
+        
+        addProperty(op, spec, l, "subBean", SubSimpleBean.class);
+        
+        ComponentPropertyProxyWorker worker = new ComponentPropertyProxyWorker();
+        
+        List exclude = new ArrayList();
+        exclude.add("Entity");
+        worker.setExcludedPackages(exclude);
+        
+        replay();
+        
+        worker.performEnhancement(op, spec);
+        
+        verify();
+        
+        IPropertySpecification prop = spec.getPropertySpecification("subBean");
+        
+        assert prop != null;
+        assert prop.isPersistent();
+        assert prop.isProxyChecked();
+        assert !prop.canProxy();
+    }
+}

Modified: tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/TaskEntryPage.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/TaskEntryPage.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/TaskEntryPage.java (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/TimeTracker/src/java/org/apache/tapestry/timetracker/page/TaskEntryPage.java Sun Feb 18 16:04:24 2007
@@ -53,7 +53,7 @@
     @InjectObject("service:timetracker.dao.ProjectDao")
     public abstract GenericDao<E> getProjectDao();
     
-    @Persist("session")
+    @Persist
     public abstract E getSelectedProject();
     
     public abstract Project getCurrentProject();

Modified: tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/Redirect.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/Redirect.html?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/Redirect.html (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/Redirect.html Sun Feb 18 16:04:24 2007
@@ -19,7 +19,7 @@
     public void redirectExternal()
     throws RequestCycleException
     {
-        throw new RedirectException("http://jakarta.apache.org/tapestry");
+        throw new RedirectException("http://tapestry.apache.org");
     }
 </pre>
 </span>

Modified: tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/localization/Change.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/localization/Change.html?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/localization/Change.html (original)
+++ tapestry/tapestry4/trunk/tapestry-examples/Workbench/src/context/localization/Change.html Sun Feb 18 16:04:24 2007
@@ -4,4 +4,4 @@
 
 <p><a jwcid="back"><img jwcid="chooseAgainImage"/></a>
 
-</span>
\ No newline at end of file
+</span>

Modified: tapestry/tapestry4/trunk/tapestry-framework/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/pom.xml?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/pom.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/pom.xml Sun Feb 18 16:04:24 2007
@@ -22,6 +22,10 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+        </dependency>
+        <dependency>
             <groupId>hivemind</groupId>
             <artifactId>hivemind</artifactId>
         </dependency>
@@ -274,7 +278,7 @@
                     </dependency>
                 </dependencies>
             </plugin>
-            
+
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>clirr-maven-plugin</artifactId>
@@ -288,7 +292,7 @@
                     </excludes>
                 </configuration>
             </plugin>
-            
+
         </plugins>
     </build>
 

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.enhance.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.enhance.xml?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.enhance.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.enhance.xml Sun Feb 18 16:04:24 2007
@@ -84,6 +84,7 @@
     <command id="inject-event-invoker" object="service:InjectEventInvokerWorker" before="abstract-property"/>
     <command id="inject-render-worker" object="service:InjectRenderWorker" before="abstract-property"/>
     <command id="specified-property" object="service:SpecifiedPropertyWorker" before="abstract-property"/>
+    <command id="inject-propertychange-worker" object="service:InjectChangeObserverWorker" before="abstract-property" />
     <command id="parameter" object="service:ParameterPropertyWorker" before="abstract-property"/>
     <command id="dispatch-inject" object="service:DispatchToInjectWorker" before="abstract-property"/>
     <command id="inject-component" object="service:InjectComponentWorker" before="abstract-property"/>
@@ -110,7 +111,6 @@
     <command id="page-validate-listener" object="service:InjectPageValidateListenerWorker" after="abstract-property"/>
     <command id="page-begin-render-listener" object="service:InjectPageBeginRenderListenerWorker" after="abstract-property"/>
     <command id="page-end-render-listener" object="service:InjectPageEndRenderListenerWorker" after="abstract-property"/>
-        
     
   </contribution>
   
@@ -314,6 +314,18 @@
     
     <invoke-factory>
       <construct class="InjectAssetWorker"/>
+    </invoke-factory>
+    
+  </service-point>
+
+  <service-point id="InjectChangeObserverWorker" interface="EnhancementWorker">
+    
+    Injects the PropertyChangeObserver service into page instances.
+    
+    <invoke-factory>
+      <construct class="InjectChangeObserverWorker" >
+        <set-service property="propertyChangeObserver" service-id="tapestry.persist.PropertyChangeObserver" />
+      </construct>
     </invoke-factory>
     
   </service-point>

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.persist.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.persist.xml?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.persist.xml (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/descriptor/META-INF/tapestry.persist.xml Sun Feb 18 16:04:24 2007
@@ -135,4 +135,15 @@
     <strategy name="client:page" object="service:PageClientPropertyPersistenceStrategy"/>
     <strategy name="client:app" object="service:AppClientPropertyPersistenceStrategy"/>
   </contribution>
-</module>
\ No newline at end of file
+  
+  <service-point id="PropertyChangeObserver" interface="PropertyChangeObserver">
+    
+    Used to observe changes to persistent page properties.
+    
+     <invoke-factory>
+      <construct class="CglibProxiedPropertyChangeObserverImpl" />
+    </invoke-factory>
+    
+  </service-point>
+  
+</module>

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/AbstractPage.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/AbstractPage.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/AbstractPage.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/AbstractPage.java Sun Feb 18 16:04:24 2007
@@ -50,7 +50,7 @@
      */
 
     private ChangeObserver _changeObserver;
-
+    
     /**
      * The {@link IEngine}the page is currently attached to.
      */

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IPage.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IPage.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IPage.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/IPage.java Sun Feb 18 16:04:24 2007
@@ -23,6 +23,7 @@
 import org.apache.tapestry.event.PageDetachListener;
 import org.apache.tapestry.event.PageEndRenderListener;
 import org.apache.tapestry.event.PageValidateListener;
+import org.apache.tapestry.record.PropertyChangeObserver;
 import org.apache.tapestry.services.ResponseBuilder;
 import org.apache.tapestry.util.ContentType;
 
@@ -63,7 +64,15 @@
      */
 
     ChangeObserver getChangeObserver();
-
+    
+    /**
+     * Returns the injected property change service responsible for monitoring changes to 
+     * individual object properties being persisted.
+     * 
+     * @return The injected property change service.
+     */
+    PropertyChangeObserver getPropertyChangeObserver();
+    
     /**
      * Returns the <code>Locale</code> of the page. The locale may be used to determine what
      * template is used by the page and the components contained by the page.

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/IPageRecorder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/IPageRecorder.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/IPageRecorder.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/engine/IPageRecorder.java Sun Feb 18 16:04:24 2007
@@ -38,7 +38,7 @@
      */
 
     void commit();
-
+    
     /**
      * Rolls back the page to the currently persisted state.
      * <p>

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/DispatchToInjectWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/DispatchToInjectWorker.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/DispatchToInjectWorker.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/DispatchToInjectWorker.java Sun Feb 18 16:04:24 2007
@@ -50,13 +50,11 @@
     {
         try
         {
-            InjectEnhancementWorker worker = (InjectEnhancementWorker) _injectWorkers.get(spec
-                    .getType());
+            InjectEnhancementWorker worker = (InjectEnhancementWorker) _injectWorkers.get(spec.getType());
 
             if (worker == null)
             {
-                _errorLog.error(EnhanceMessages.unknownInjectType(spec.getProperty(), spec
-                        .getType()), spec.getLocation(), null);
+                _errorLog.error(EnhanceMessages.unknownInjectType(spec.getProperty(), spec.getType()), spec.getLocation(), null);
                 return;
             }
 
@@ -65,8 +63,8 @@
         }
         catch (Exception ex)
         {
-            _errorLog.error(EnhanceMessages.errorAddingProperty(spec.getProperty(), op
-                    .getBaseClass(), ex), spec.getLocation(), ex);
+            _errorLog.error(EnhanceMessages.errorAddingProperty(spec.getProperty(), op.getBaseClass(), ex), 
+                    spec.getLocation(), ex);
         }
     }
 

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhanceUtils.java Sun Feb 18 16:04:24 2007
@@ -18,6 +18,9 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.Factory;
+
 import org.apache.hivemind.ApplicationRuntimeException;
 import org.apache.hivemind.Location;
 import org.apache.hivemind.service.ClassFabUtils;
@@ -48,7 +51,7 @@
     public static final MethodSignature CLEANUP_AFTER_RENDER_SIGNATURE = new MethodSignature(
             void.class, "cleanupAfterRender", new Class[]
             { IRequestCycle.class }, null);
-
+    
     /**
      * Used to unwrap primitive types inside the accessor method. In each case, the binding is in a
      * variable named "binding", and {0} will be the actual type of the property. The Map is keyed
@@ -91,12 +94,9 @@
             String propertyName, Class propertyType, Location location)
     {
         String methodName = op.getAccessorMethodName(propertyName);
-
-        op.addMethod(
-                Modifier.PUBLIC,
-                new MethodSignature(propertyType, methodName, null, null),
-                "return " + fieldName + ";",
-                location);
+        
+        op.addMethod( Modifier.PUBLIC, new MethodSignature(propertyType, methodName, null, null),
+                "return " + fieldName + ";", location);
     }
 
     public static void createSimpleMutator(EnhancementOperation op, String fieldName,
@@ -104,8 +104,8 @@
     {
         String methodName = createMutatorMethodName(propertyName);
 
-        op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, methodName, new Class[]
-        { propertyType }, null), fieldName + " = $1;", location);
+        op.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, methodName, 
+                new Class[] { propertyType }, null), fieldName + " = $1;", location);
     }
 
     /**
@@ -129,13 +129,13 @@
     {
         Defense.notNull(op, "op");
         Defense.notNull(propertyName, "propertyName");
-
+        
         if (definedTypeName != null)
         {
             Class propertyType = op.convertTypeName(definedTypeName);
-
+            
             op.validateProperty(propertyName, propertyType);
-
+            
             return propertyType;
         }
         
@@ -228,8 +228,7 @@
      *            the type of value to be extracted from the binding.
      */
 
-    public static String createUnwrapExpression(EnhancementOperation op, String bindingName,
-            Class valueType)
+    public static String createUnwrapExpression(EnhancementOperation op, String bindingName, Class valueType)
     {
         Defense.notNull(op, "op");
         Defense.notNull(bindingName, "bindingName");
@@ -263,7 +262,7 @@
 
         return buffer.toString();
     }
-
+    
     /**
      * Verifies that a property type can be assigned a particular type of value.
      * 
@@ -298,5 +297,52 @@
                     requiredType));
 
         return propertyType;
+    }
+    
+    /**
+     * Determines whether or not the specified class type is elligable for proxying. This generally
+     * means it needs a default constructor, can't be final / primitive / array. 
+     * 
+     * @param type 
+     *          The class to check for proxying elligibility.
+     * @return True if the type can be proxied, false otherwise.
+     */
+    public static boolean canProxyPropertyType(Class type)
+    {
+        // if it's already enhanced it must be by someone else
+        
+        if (Enhancer.isEnhanced(type) || Factory.class.isAssignableFrom(type))
+            return false;
+        
+        if (type.isInterface())
+            return true;
+        
+        if (!hasEmptyConstructor(type))
+            return false;
+        
+        if (type.isArray() || type.isPrimitive() || Modifier.isFinal(type.getModifiers()) || Object.class == type)
+            return false;
+        
+        return true;
+    }
+    
+    /**
+     * Checks if the specified class type has an empty constructor.
+     * 
+     * @param type
+     *          The class to check, can't be null.
+     *          
+     * @return True if a no args constructor exists.
+     */
+    public static boolean hasEmptyConstructor(Class type)
+    {
+        Defense.notNull(type, "type");
+        
+        try {
+            
+            return type.getConstructor(null) != null;
+        } catch (Throwable t) {
+            return false;
+        }
     }
 }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java Sun Feb 18 16:04:24 2007
@@ -96,8 +96,7 @@
      *         prevent naming conflicts.
      */
 
-    String addInjectedField(String fieldName, Class fieldType,
-            Object value);
+    String addInjectedField(String fieldName, Class fieldType, Object value);
 
     /**
      * Converts a type name (an object class name, a primtive name, or an array)

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java Sun Feb 18 16:04:24 2007
@@ -235,7 +235,8 @@
 
             String name = pd.getName();
 
-            if (!_properties.containsKey(name)) _properties.put(name, pd);
+            if (!_properties.containsKey(name)) 
+                _properties.put(name, pd);
         }
     }
 
@@ -244,8 +245,7 @@
         Defense.notNull(propertyName, "propertyName");
 
         if (_claimedProperties.contains(propertyName))
-            throw new ApplicationRuntimeException(EnhanceMessages
-                    .claimedProperty(propertyName));
+            throw new ApplicationRuntimeException(EnhanceMessages.claimedProperty(propertyName));
 
         _claimedProperties.add(propertyName);
     }
@@ -260,7 +260,8 @@
         
         PropertyDescriptor pd = getPropertyDescriptor(propertyName);
         
-        if (pd == null) return false;
+        if (pd == null) 
+            return false;
         
         return pd.getWriteMethod() == null ? true : false;
     }
@@ -272,8 +273,7 @@
         PropertyDescriptor pd = getPropertyDescriptor(propertyName);
         
         if (pd != null && pd.getWriteMethod() != null)
-            throw new ApplicationRuntimeException(EnhanceMessages
-                    .readonlyProperty(propertyName, pd.getWriteMethod()));
+            throw new ApplicationRuntimeException(EnhanceMessages.readonlyProperty(propertyName, pd.getWriteMethod()));
     }
 
     public void addField(String name, Class type)
@@ -281,8 +281,7 @@
         _classFab.addField(name, type);
     }
 
-    public String addInjectedField(String fieldName, Class fieldType,
-            Object value)
+    public String addInjectedField(String fieldName, Class fieldType, Object value)
     {
         Defense.notNull(fieldName, "fieldName");
         Defense.notNull(fieldType, "fieldType");
@@ -292,7 +291,8 @@
 
         // See if this object has been previously added.
 
-        if (existing != null) return existing;
+        if (existing != null) 
+            return existing;
 
         // TODO: Should be ensure that the name is unique?
 
@@ -310,8 +310,7 @@
 
         int parameterIndex = addConstructorParameter(fieldType, value);
 
-        constructorBuilder().addln("{0} = ${1};", uniqueName,
-                Integer.toString(parameterIndex));
+        constructorBuilder().addln("{0} = ${1};", uniqueName, Integer.toString(parameterIndex));
 
         // Remember the mapping from the value to the field name.
 
@@ -352,15 +351,15 @@
 
         PropertyDescriptor pd = getPropertyDescriptor(name);
 
-        if (pd == null) return;
+        if (pd == null) 
+            return;
 
         Class propertyType = pd.getPropertyType();
 
-        if (propertyType.equals(expectedType)) return;
+        if (propertyType.equals(expectedType)) 
+            return;
 
-        throw new ApplicationRuntimeException(EnhanceMessages
-                .propertyTypeMismatch(_baseClass, name, propertyType,
-                        expectedType));
+        throw new ApplicationRuntimeException(EnhanceMessages.propertyTypeMismatch(_baseClass, name, propertyType, expectedType));
     }
     
     PropertyDescriptor getPropertyDescriptor(String name)
@@ -389,8 +388,7 @@
 
         Location existing = (Location) _methods.get(sig);
         if (existing != null)
-            throw new ApplicationRuntimeException(EnhanceMessages
-                    .methodConflict(sig, existing), location, null);
+            throw new ApplicationRuntimeException(EnhanceMessages.methodConflict(sig, existing), location, null);
 
         _methods.put(sig, location);
 
@@ -408,7 +406,8 @@
 
         String result = (String) _finalFields.get(clazz);
 
-        if (result == null) result = addClassReference(clazz);
+        if (result == null) 
+            result = addClassReference(clazz);
 
         return result;
     }
@@ -476,8 +475,7 @@
         }
         catch (Throwable t)
         {
-            throw new ApplicationRuntimeException(EnhanceMessages
-                    .classEnhancementFailure(_baseClass, t), _classFab, null, t);
+            throw new ApplicationRuntimeException(EnhanceMessages.classEnhancementFailure(_baseClass, t), _classFab, null, t);
         }
     }
 
@@ -489,14 +487,13 @@
         {
             _constructorBuilder.end();
 
-            Class[] types = (Class[]) _constructorTypes
-                    .toArray(new Class[_constructorTypes.size()]);
+            Class[] types = (Class[]) _constructorTypes.toArray(new Class[_constructorTypes.size()]);
 
-            _classFab.addConstructor(types, null, _constructorBuilder
-                    .toString());
+            _classFab.addConstructor(types, null, _constructorBuilder.toString());
         }
 
-        if (_log != null) _log.debug("Creating class:\n\n" + _classFab);
+        if (_log != null && _log.isDebugEnabled()) 
+            _log.debug("Creating class:\n\n" + _classFab);
     }
 
     private void finalizeIncompleteMethods()
@@ -539,8 +536,7 @@
     {
         addInterfaceIfNeeded(interfaceClass);
 
-        BodyBuilder builder = (BodyBuilder) _incompleteMethods
-                .get(methodSignature);
+        BodyBuilder builder = (BodyBuilder) _incompleteMethods.get(methodSignature);
 
         if (builder == null)
         {
@@ -554,7 +550,8 @@
 
     private void addInterfaceIfNeeded(Class interfaceClass)
     {
-        if (implementsInterface(interfaceClass)) return;
+        if (implementsInterface(interfaceClass)) 
+            return;
 
         _classFab.addInterface(interfaceClass);
         _addedInterfaces.add(interfaceClass);
@@ -562,14 +559,16 @@
 
     public boolean implementsInterface(Class interfaceClass)
     {
-        if (interfaceClass.isAssignableFrom(_baseClass)) return true;
+        if (interfaceClass.isAssignableFrom(_baseClass)) 
+            return true;
 
         Iterator i = _addedInterfaces.iterator();
         while(i.hasNext())
         {
             Class addedInterface = (Class) i.next();
 
-            if (interfaceClass.isAssignableFrom(addedInterface)) return true;
+            if (interfaceClass.isAssignableFrom(addedInterface)) 
+                return true;
         }
 
         return false;
@@ -650,9 +649,11 @@
 
             String name = pd.getName();
 
-            if (_claimedProperties.contains(name)) continue;
+            if (_claimedProperties.contains(name)) 
+                continue;
 
-            if (isAbstractProperty(pd)) result.add(name);
+            if (isAbstractProperty(pd)) 
+                result.add(name);
         }
 
         return result;

Added: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectChangeObserverWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectChangeObserverWorker.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectChangeObserverWorker.java (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectChangeObserverWorker.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,75 @@
+// Copyright 2004, 2005 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.enhance;
+
+import org.apache.hivemind.ErrorLog;
+import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.record.PropertyChangeObserver;
+import org.apache.tapestry.spec.IComponentSpecification;
+
+
+/**
+ * Responsible for injecting the {@link PropertyChangeObserver} service into each page
+ * instance.
+ */
+public class InjectChangeObserverWorker implements EnhancementWorker
+{
+    static final String PROPERTY_NAME = "propertyChangeObserver";
+    
+    private ErrorLog _errorLog;
+    
+    private PropertyChangeObserver _observer;
+    
+    /**
+     * {@inheritDoc}
+     */
+    public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
+    {
+        if (!spec.isPageSpecification())
+            return;
+        
+        try
+        {
+            injectChangeObserver(op, spec);
+        }
+        catch (Exception ex)
+        {
+            _errorLog.error(EnhanceMessages.errorAddingProperty(PROPERTY_NAME, op.getBaseClass(), ex), 
+                    spec.getLocation(), ex);
+        }
+    }
+    
+    public void injectChangeObserver(EnhancementOperation op, IComponentSpecification spec)
+    {
+        Defense.notNull(op, "op");
+        Defense.notNull(spec, "spec");
+        
+        op.claimReadonlyProperty(PROPERTY_NAME);
+        
+        String fieldName = op.addInjectedField("_$" + PROPERTY_NAME, PropertyChangeObserver.class, _observer);
+        
+        EnhanceUtils.createSimpleAccessor(op, fieldName, PROPERTY_NAME, 
+                PropertyChangeObserver.class, spec.getLocation());
+    }
+    
+    public void setErrorLog(ErrorLog errorLog)
+    {
+        _errorLog = errorLog;
+    }
+    
+    public void setPropertyChangeObserver(PropertyChangeObserver observer)
+    {
+        _observer = observer;
+    }
+}

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectRenderWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectRenderWorker.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectRenderWorker.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/InjectRenderWorker.java Sun Feb 18 16:04:24 2007
@@ -43,9 +43,8 @@
         }
         catch (Exception ex)
         {
-            _errorLog.error(EnhanceMessages.errorAddingProperty(
-                    PROPERTY_NAME, op.getBaseClass(), ex), spec
-                    .getLocation(), ex);
+            _errorLog.error(EnhanceMessages.errorAddingProperty(PROPERTY_NAME, op.getBaseClass(), ex), 
+                    spec.getLocation(), ex);
         }
     }
     
@@ -56,9 +55,7 @@
         
         op.claimReadonlyProperty(PROPERTY_NAME);
         
-        String fieldName = op.addInjectedField("_$"
-                + PROPERTY_NAME, ComponentRenderWorker.class,
-                _renderWorker);
+        String fieldName = op.addInjectedField("_$" + PROPERTY_NAME, ComponentRenderWorker.class, _renderWorker);
         
         EnhanceUtils.createSimpleAccessor(op, fieldName,
                 PROPERTY_NAME, ComponentRenderWorker.class,

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/enhance/SpecifiedPropertyWorker.java Sun Feb 18 16:04:24 2007
@@ -20,6 +20,7 @@
 import org.apache.hivemind.ErrorLog;
 import org.apache.hivemind.Location;
 import org.apache.hivemind.service.BodyBuilder;
+import org.apache.hivemind.service.ClassFabUtils;
 import org.apache.hivemind.service.MethodSignature;
 import org.apache.hivemind.util.Defense;
 import org.apache.tapestry.IBinding;
@@ -31,7 +32,8 @@
 
 /**
  * Responsible for adding properties to a class corresponding to specified
- * properties in the component's specification.
+ * properties in the component's specification - which may come from .jwc / .page specifications
+ * or annotated abstract methods.
  * 
  * @author Howard M. Lewis Ship
  * @since 4.0
@@ -43,7 +45,7 @@
     private ErrorLog _errorLog;
 
     private BindingSource _bindingSource;
-
+    
     /**
      * Iterates over the specified properties, creating an enhanced property for
      * each (a field, an accessor, a mutator). Persistent properties will invoke
@@ -83,17 +85,15 @@
         boolean persistent = ps.isPersistent();
         String initialValue = ps.getInitialValue();
         Location location = ps.getLocation();
-
-        addProperty(op, propertyName, specifiedType, persistent, initialValue,
-                location);
+        
+        addProperty(op, propertyName, specifiedType, persistent, initialValue, location, ps);
     }
 
-    public void addProperty(EnhancementOperation op, String propertyName,
-            String specifiedType, boolean persistent, String initialValue,
-            Location location)
+    public void addProperty(EnhancementOperation op, String propertyName, String specifiedType, 
+            boolean persistent, String initialValue, Location location, IPropertySpecification ps)
     {
         Class propertyType = EnhanceUtils.extractPropertyType(op, propertyName, specifiedType);
-
+        
         op.claimProperty(propertyName);
 
         String field = "_$" + propertyName;
@@ -104,83 +104,86 @@
         // if they exist. 4.0 is less picky ... it blindly adds new methods,
         // possibly
         // overwriting methods in the base component class.
-
-        EnhanceUtils.createSimpleAccessor(op, field, propertyName,
-                propertyType, location);
-
-        addMutator(op, propertyName, propertyType, field, persistent, location);
-
+        
+        EnhanceUtils.createSimpleAccessor(op, field, propertyName, propertyType, location);
+        
+        boolean canProxy = false;
+        if (ps.isProxyChecked())
+            canProxy = ps.canProxy();
+        else
+            canProxy = persistent && EnhanceUtils.canProxyPropertyType(propertyType);
+        
+        addMutator(op, propertyName, propertyType, field, persistent, canProxy, location);
+        
         if (initialValue == null)
             addReinitializer(op, propertyType, field);
-        else addInitialValue(op, propertyName, propertyType, field,
-                initialValue, location);
+        else 
+            addInitialValue(op, propertyName, propertyType, field, initialValue, persistent, canProxy, location);
     }
 
-    private void addReinitializer(EnhancementOperation op, Class propertyType,
-            String fieldName)
+    private void addReinitializer(EnhancementOperation op, Class propertyType, String fieldName)
     {
         String defaultFieldName = fieldName + "$default";
-
+        
         op.addField(defaultFieldName, propertyType);
-
+        
         // On finishLoad(), store the current value into the default field.
 
-        op.extendMethodImplementation(IComponent.class,
-                EnhanceUtils.FINISH_LOAD_SIGNATURE, defaultFieldName + " = "
-                        + fieldName + ";");
+        op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, 
+                defaultFieldName + " = " + fieldName + ";");
 
         // On pageDetach(), restore the attribute to its default value.
-
+        
         op.extendMethodImplementation(PageDetachListener.class,
-                EnhanceUtils.PAGE_DETACHED_SIGNATURE, fieldName + " = "
-                        + defaultFieldName + ";");
+                EnhanceUtils.PAGE_DETACHED_SIGNATURE, fieldName + " = " + defaultFieldName + ";");
     }
 
-    private void addInitialValue(EnhancementOperation op, String propertyName,
-            Class propertyType, String fieldName, String initialValue,
-            Location location)
+    private void addInitialValue(EnhancementOperation op, String propertyName, Class propertyType, 
+            String fieldName, String initialValue, boolean persistent, boolean canProxy, Location location)
     {
-        String description = EnhanceMessages
-                .initialValueForProperty(propertyName);
-
-        InitialValueBindingCreator creator = new InitialValueBindingCreator(
-                _bindingSource, description, initialValue, location);
+        String description = EnhanceMessages.initialValueForProperty(propertyName);
 
-        String creatorField = op.addInjectedField(fieldName
-                + "$initialValueBindingCreator",
-                InitialValueBindingCreator.class, creator);
+        InitialValueBindingCreator creator = 
+            new InitialValueBindingCreator(_bindingSource, description, initialValue, location);
+        
+        String creatorField = op.addInjectedField(fieldName + "$initialValueBindingCreator", InitialValueBindingCreator.class, creator);
 
         String bindingField = fieldName + "$initialValueBinding";
         op.addField(bindingField, IBinding.class);
 
         BodyBuilder builder = new BodyBuilder();
+        
+        builder.addln("{0} = {1}.createBinding(this);", bindingField, creatorField);
 
-        builder.addln("{0} = {1}.createBinding(this);", bindingField,
-                creatorField);
-
-        op.extendMethodImplementation(IComponent.class,
-                EnhanceUtils.FINISH_LOAD_SIGNATURE, builder.toString());
+        op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, builder.toString());
 
         builder.clear();
-
-        builder.addln("{0} = {1};", fieldName, EnhanceUtils
-                .createUnwrapExpression(op, bindingField, propertyType));
-
+        
+        builder.addln("{0} = {1};", fieldName, EnhanceUtils.createUnwrapExpression(op, bindingField, propertyType));
+        
+        // add proxy observers if we can
+        
+        if (canProxy)  {
+            
+            builder.add(fieldName + " = (" + ClassFabUtils.getJavaClassName(propertyType) + ") getPage().getPropertyChangeObserver().observePropertyChanges(this, (" 
+                    + ClassFabUtils.getJavaClassName(propertyType) + ") " + fieldName + ",");
+            builder.addQuoted(propertyName);
+            builder.addln(");");
+        }
+        
         String code = builder.toString();
-
+        
         // In finishLoad() and pageDetach(), de-reference the binding to get the
         // value
         // for the property.
 
-        op.extendMethodImplementation(IComponent.class,
-                EnhanceUtils.FINISH_LOAD_SIGNATURE, code);
-        op.extendMethodImplementation(PageDetachListener.class,
-                EnhanceUtils.PAGE_DETACHED_SIGNATURE, code);
-
+        op.extendMethodImplementation(IComponent.class, EnhanceUtils.FINISH_LOAD_SIGNATURE, code);
+        
+        op.extendMethodImplementation(PageDetachListener.class, EnhanceUtils.PAGE_DETACHED_SIGNATURE, code);
     }
 
     private void addMutator(EnhancementOperation op, String propertyName,
-            Class propertyType, String fieldName, boolean persistent,
+            Class propertyType, String fieldName, boolean persistent,  boolean canProxy, 
             Location location)
     {
         String methodName = EnhanceUtils.createMutatorMethodName(propertyName);
@@ -188,20 +191,30 @@
         BodyBuilder body = new BodyBuilder();
 
         body.begin();
-
-        if (persistent)
-        {
+        
+        if (persistent) {
+            
             body.add("org.apache.tapestry.Tapestry#fireObservedChange(this, ");
             body.addQuoted(propertyName);
             body.addln(", ($w) $1);");
         }
-
-        body.addln(fieldName + " = $1;");
-
+        
+        if (canProxy) {
+            
+            // set the field to the proxied type
+            
+            body.add(fieldName + " = (" + ClassFabUtils.getJavaClassName(propertyType) + ") getPage().getPropertyChangeObserver().observePropertyChanges(this, ($w) $1,");
+            body.addQuoted(propertyName);
+            body.addln(");");
+            
+        } else {
+            
+            body.addln(fieldName + " = $1;");
+        }
+        
         body.end();
 
-        MethodSignature sig = new MethodSignature(void.class, methodName,
-                new Class[] { propertyType }, null);
+        MethodSignature sig = new MethodSignature(void.class, methodName, new Class[] { propertyType }, null);
 
         op.addMethod(Modifier.PUBLIC, sig, body.toString(), location);
     }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ChangeObserver.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ChangeObserver.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ChangeObserver.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ChangeObserver.java Sun Feb 18 16:04:24 2007
@@ -33,4 +33,11 @@
      **/
 
     void observeChange(ObservedChangeEvent event);
+    
+    /**
+     * Checks if the current state changes have already been committed.
+     * 
+     * @return True if state changes are locked, false otherwise.
+     */
+    boolean isLocked();
 }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ObservedChangeEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ObservedChangeEvent.java?view=diff&rev=509035&r1=509034&r2=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ObservedChangeEvent.java (original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/event/ObservedChangeEvent.java Sun Feb 18 16:04:24 2007
@@ -54,8 +54,7 @@
      *             serializable
      */
 
-    public ObservedChangeEvent(IComponent component, String propertyName,
-            Object newValue)
+    public ObservedChangeEvent(IComponent component, String propertyName, Object newValue)
     {
         super(component);
 

Added: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibPropertyChangeInterceptor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibPropertyChangeInterceptor.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibPropertyChangeInterceptor.java (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibPropertyChangeInterceptor.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,71 @@
+// Copyright 2004, 2005 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.record;
+
+import java.lang.reflect.Method;
+
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+
+import org.apache.tapestry.IComponent;
+import org.apache.tapestry.Tapestry;
+
+
+/**
+ * Class responsible for watching changes on a specific property instance attached to a component. 
+ * 
+ * Used by the default {@link PropertyChangeObserver} service.
+ */
+public class CglibPropertyChangeInterceptor implements MethodInterceptor
+{
+    private Object _property;
+    
+    private transient IComponent _component;
+    private String _propertyName;
+    
+    /**
+     * Creates a new property change observer for the specified component / property.
+     * 
+     * @param component
+     *          The component that owns this property.
+     * @param property
+     *          The actual property object, may be null.
+     * @param propertyName
+     *          The name of the property.
+     */
+    public CglibPropertyChangeInterceptor(IComponent component, Object property, String propertyName)
+    {
+        _property = property;
+        _component = component;
+        _propertyName = propertyName;
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
+        throws Throwable
+    {
+        if (_component.getPage().getChangeObserver() != null
+                && !_component.getPage().getChangeObserver().isLocked()) {
+            
+            Tapestry.fireObservedChange(_component, _propertyName, _property);
+        }
+        
+        // invoke the method being called either way
+        
+        return proxy.invoke(_property, args);
+    }
+
+}

Added: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibProxiedPropertyChangeObserverImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibProxiedPropertyChangeObserverImpl.java?view=auto&rev=509035
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibProxiedPropertyChangeObserverImpl.java (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/record/CglibProxiedPropertyChangeObserverImpl.java Sun Feb 18 16:04:24 2007
@@ -0,0 +1,97 @@
+// Copyright 2004, 2005 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.record;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.Factory;
+
+import org.apache.hivemind.util.Defense;
+import org.apache.tapestry.IComponent;
+import org.apache.tapestry.enhance.EnhanceUtils;
+
+
+/**
+ * Default implementation of {@link PropertyChangeObserver} that creates a bytecode enhanced
+ * proxy instance of {@link CglibPropertyChangeInterceptor} to notify Tapestry of changes
+ * made to the first level public properties available on the observed object.
+ */
+public class CglibProxiedPropertyChangeObserverImpl implements PropertyChangeObserver
+{
+    
+    /**
+     * Holds a mapping of previously enhanced property class proxy {@link Factory} objects
+     * keyed off of {@link IComponent#getIdPath()} + <code>propertyName</code>.
+     */
+    private Map _enhancedMap = new WeakHashMap();
+    
+    /**
+     * Holds previously reflected {@link Class#getName()} values on any property instances
+     * that don't have default constructors, which we can't enhance.
+     */
+    private Map _badMap = new WeakHashMap();
+    
+    public CglibProxiedPropertyChangeObserverImpl()
+    {
+    }
+    
+    /**
+     * {@inheritDoc}
+     */
+    public Object observePropertyChanges(IComponent component, Object property, String propertyName)
+    {
+        Defense.notNull(component, "component");
+        
+        // can't proxy a null object
+        if (property == null)
+            return null;
+        
+        // if it's already proxied just return it
+        
+        if (Enhancer.isEnhanced(property.getClass()))
+            return property;
+        
+        // possibly already did the expensive reflection on this type
+        
+        if (_badMap.get(property.getClass().getName()) != null)
+            return property;
+        
+        Factory f = (Factory) _enhancedMap.get(component.getIdPath() + propertyName);
+        
+        if (f == null) {
+            
+            // some classes can't have their constructors detected until now..
+            
+            if (!EnhanceUtils.hasEmptyConstructor(property.getClass())) {
+                
+                _badMap.put(property.getClass().getName(), Boolean.TRUE);
+                return property;
+            }
+
+            Object ret = Enhancer.create(property.getClass(), property.getClass().getInterfaces(), 
+                    new ObservableMethodFilter(), 
+                    new Callback[] { new LazyProxyDelegate(property), new CglibPropertyChangeInterceptor(component, property, propertyName)});
+            
+            f = (Factory)ret;
+            _enhancedMap.put(component.getIdPath() + propertyName, f);
+            
+            return ret;
+        }
+        
+        return f.newInstance(new Callback[] { new LazyProxyDelegate(property), new CglibPropertyChangeInterceptor(component, property, propertyName)});
+    }
+}