You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/05/29 08:42:13 UTC

[causeway] branch CAUSEWAY-2485 updated (e9ad1a5b82 -> 13f9166b1e)

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a change to branch CAUSEWAY-2485
in repository https://gitbox.apache.org/repos/asf/causeway.git


    from e9ad1a5b82 CAUSEWAY-2485: wip on action layout redirect policy
     add a4d0bf3d44 CAUSEWAY-3469 : mark 'canRemove' method of ApplicationUser_removeRoles mixin as programmatic
     add cb2cd9d6ab CAUSEWAY-3470: hot-fix: don't process mixed in actions (that were mixed in to the mixin under validation)
     add 211b2821e2 Bump togglz.version from 3.3.3 to 4.0.1
     add b5bf7be756 Merge pull request #1685 from apache/dependabot/maven/master/togglz.version-4.0.1
     add ede1f846e2 Bump guava from 31.1-jre to 32.0.0-jre
     add 741e4e88ea Merge pull request #1686 from apache/dependabot/maven/master/com.google.guava-guava-32.0.0-jre
     new 13f9166b1e CAUSEWAY-2485: reworks @ActionLayout#redirectPolicy and @PropertyLayout#repainting

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 bom/pom.xml                                        |  4 +-
 .../all/MixinSanityChecksValidator.java            |  1 +
 .../PdfJsViewerAdvisorFallback.java                |  8 +-
 ...tionLayoutRedirectPolicyEntity-description.adoc | 35 +++++++-
 .../ActionLayoutRedirectPolicyEntity.java          | 18 ++--
 .../ActionLayoutRedirectPolicyEntity.layout.xml    | 28 ++++---
 ...ctPolicyEntity_incrementRedirectEvenIfSame.java |  7 +-
 ...olicyEntity_incrementRedirectOnlyIfDiffers.java |  9 +-
 ...ayoutRedirectPolicyPage#objects.columnOrder.txt |  3 +
 ...ActionLayoutRedirectPolicyPage-description.adoc | 98 +++++++++++-----------
 ...LayoutRedirectPolicyEntityImpl-description.adoc |  0
 .../ActionLayoutRedirectPolicyEntityImpl.java      | 40 +++++----
 ...onLayoutRedirectPolicyEntityImplRepository.java | 32 ++++++-
 .../jpa/ActionLayoutRedirectPolicyEntityImpl.java  | 30 +++++--
 ...onLayoutRedirectPolicyEntityImplRepository.java | 21 ++++-
 .../PropertyLayout/PropertyLayoutMenu.java         | 13 +--
 .../PropertyLayoutRepaintingPage-description.adoc  | 31 +------
 .../repainting/PropertyLayoutRepaintingPage.java   | 20 -----
 .../PropertyLayoutRepaintingPage.layout.xml        | 16 +---
 .../ValueSemantics/ValueSemanticsMenu.java         | 13 +++
 .../ValueSemanticsDateRenderAdjustDaysPage.java    |  2 +-
 .../ValueSemantics/percentage/Percentage.java      | 19 +++++
 .../ValueSemanticsProviderForPercentage.java       | 26 ++++++
 ...emanticsProviderPercentagePage-description.adoc |  6 +-
 .../ValueSemanticsProviderPercentagePage.java}     | 34 ++++----
 ...alueSemanticsProviderPercentagePage.layout.xml} |  0
 .../src/main/java/demoapp/dom/menubars.layout.xml  |  1 +
 .../dom/mixins/ApplicationUser_removeRoles.java    |  3 +-
 28 files changed, 300 insertions(+), 218 deletions(-)
 rename examples/demo/domain/src/main/java/demoapp/dom/{domain/properties/PropertyLayout/repainting => _infra}/PdfJsViewerAdvisorFallback.java (92%)
 create mode 100644 examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage#objects.columnOrder.txt
 copy examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/{jpa => jdo}/ActionLayoutRedirectPolicyEntityImpl-description.adoc (100%)
 copy examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/{jpa => jdo}/ActionLayoutRedirectPolicyEntityImpl.java (70%)
 copy examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/{jpa => jdo}/ActionLayoutRedirectPolicyEntityImplRepository.java (64%)
 create mode 100644 examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/Percentage.java
 create mode 100644 examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderForPercentage.java
 copy valuetypes/asciidoc/adoc/modules/asciidoc/pages/about.adoc => examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage-description.adoc (95%)
 copy examples/demo/domain/src/main/java/demoapp/dom/domain/{actions/ActionLayout/sequence/child/ActionLayoutSequenceChildVm.java => properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.java} (66%)
 copy examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/{dateRenderAdjustDays/ValueSemanticsDateRenderAdjustDaysPage.layout.xml => percentage/ValueSemanticsProviderPercentagePage.layout.xml} (100%)


[causeway] 01/01: CAUSEWAY-2485: reworks @ActionLayout#redirectPolicy and @PropertyLayout#repainting

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-2485
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 13f9166b1e574a66306f85dbe0f4a7d024ff95c2
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Mon May 29 09:39:35 2023 +0100

    CAUSEWAY-2485: reworks @ActionLayout#redirectPolicy and @PropertyLayout#repainting
---
 .../PdfJsViewerAdvisorFallback.java                |  8 +-
 ...tionLayoutRedirectPolicyEntity-description.adoc | 35 +++++++-
 .../ActionLayoutRedirectPolicyEntity.java          | 18 ++--
 .../ActionLayoutRedirectPolicyEntity.layout.xml    | 28 ++++---
 ...ctPolicyEntity_incrementRedirectEvenIfSame.java |  7 +-
 ...olicyEntity_incrementRedirectOnlyIfDiffers.java |  9 +-
 ...ayoutRedirectPolicyPage#objects.columnOrder.txt |  3 +
 ...ActionLayoutRedirectPolicyPage-description.adoc | 98 +++++++++++-----------
 ...LayoutRedirectPolicyEntityImpl-description.adoc |  3 +
 .../ActionLayoutRedirectPolicyEntityImpl.java      | 40 +++++----
 ...onLayoutRedirectPolicyEntityImplRepository.java | 32 ++++++-
 .../jpa/ActionLayoutRedirectPolicyEntityImpl.java  | 30 +++++--
 ...onLayoutRedirectPolicyEntityImplRepository.java | 21 ++++-
 .../PropertyLayout/PropertyLayoutMenu.java         | 13 +--
 .../PropertyLayoutRepaintingPage-description.adoc  | 31 +------
 .../repainting/PropertyLayoutRepaintingPage.java   | 20 -----
 .../PropertyLayoutRepaintingPage.layout.xml        | 16 +---
 .../ValueSemantics/ValueSemanticsMenu.java         | 13 +++
 .../ValueSemanticsDateRenderAdjustDaysPage.java    |  2 +-
 .../ValueSemantics/percentage/Percentage.java      | 19 +++++
 .../ValueSemanticsProviderForPercentage.java       | 26 ++++++
 ...emanticsProviderPercentagePage-description.adoc |  7 ++
 .../ValueSemanticsProviderPercentagePage.java      | 60 +++++++++++++
 ...alueSemanticsProviderPercentagePage.layout.xml} | 18 ++--
 .../src/main/java/demoapp/dom/menubars.layout.xml  |  1 +
 25 files changed, 349 insertions(+), 209 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PdfJsViewerAdvisorFallback.java b/examples/demo/domain/src/main/java/demoapp/dom/_infra/PdfJsViewerAdvisorFallback.java
similarity index 92%
rename from examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PdfJsViewerAdvisorFallback.java
rename to examples/demo/domain/src/main/java/demoapp/dom/_infra/PdfJsViewerAdvisorFallback.java
index 7aaf8ba8a8..10e2bf1c9e 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PdfJsViewerAdvisorFallback.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/_infra/PdfJsViewerAdvisorFallback.java
@@ -16,19 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.domain.properties.PropertyLayout.repainting;
-
-import org.springframework.stereotype.Service;
+package demoapp.dom._infra;
 
 import org.apache.causeway.extensions.pdfjs.applib.config.Scale;
 import org.apache.causeway.extensions.pdfjs.applib.spi.PdfJsViewerAdvisor;
 
+import org.springframework.stereotype.Service;
+
 @Service
 public class PdfJsViewerAdvisorFallback implements PdfJsViewerAdvisor {
 
     @Override
     public Advice advise(final InstanceKey instanceKey) {
-        return new Advice(1, Scale._1_00, 400);
+        return new Advice(1, Scale._1_00, 800);
     }
 
     @Override
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity-description.adoc
index 942da34a35..a14fe25829 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity-description.adoc
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity-description.adoc
@@ -1,7 +1,38 @@
 :Notice: Licensed to the Apache 1Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 a [...]
 
-This domain class is used in the demo of `@ActionLayout#redirectPolicy`.
+This domain class is used in the demo of `@ActionLayout#redirectPolicy` (in combination with `@PropertyLayout#repainting`.
 
 == How this demo works
 
-TODO
+This entity has a property holding an immutable PDF blob, and which is annotated with `@PropertyLayout#repainting` to suppress unnecessary repainting.
+There are also two mixin actions that are used to increment its counter, both of which return the same entity.
+One of these actions uses `@ActionLayout#redirectPolicy` to only update the page and so not redraw the PDF.
+
+In terms of code:
+
+* PDF blob
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity.java[tags=class]
+----
+<.> render the blob as a PDF
+<.> don't repaint the PDF if possible
+
+* redirect even if the same
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java[tags=class]
+----
+<.> suppress redirect if returning the same object
+<.> return the same object
+
+* redirect even if the same
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java[tags=class]
+----
+<.> redirect _even if_ returning the same object
+
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.java
index f4440d76b5..b13f0d3bab 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.java
@@ -30,6 +30,9 @@ import org.apache.causeway.applib.annotation.Property;
 import org.apache.causeway.applib.annotation.PropertyLayout;
 import org.apache.causeway.applib.annotation.Repainting;
 import org.apache.causeway.applib.services.clock.ClockService;
+import org.apache.causeway.applib.value.Blob;
+import org.apache.causeway.extensions.pdfjs.applib.annotations.PdfJsViewer;
+import org.apache.causeway.persistence.jpa.applib.types.BlobJpaEmbeddable;
 
 import javax.inject.Inject;
 import javax.xml.bind.annotation.XmlElement;
@@ -61,15 +64,14 @@ public abstract class ActionLayoutRedirectPolicyEntity
     public abstract Integer getCount();
     public abstract void setCount(Integer value);
 
-    @SneakyThrows
+//tag::class[]
+    @PdfJsViewer                                            // <.>
     @Property
-    @PropertyLayout(repainting = Repainting.NO_REPAINT) // <.>
-    public String getCurrentTime() {
-        Thread.sleep(1000);
-        return clockService.getClock().nowAsLocalDateTime().toString();
-    }
-
-    @Inject private ClockService clockService;
+    @PropertyLayout(repainting = Repainting.NO_REPAINT)     // <.>
+    public abstract Blob getBlob();
+    // ...
+//end::class[]
+    public abstract void setBlob(final Blob blob);
 
 //tag::class[]
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.layout.xml
index b29891343c..cc52ac618c 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity.layout.xml
@@ -30,18 +30,9 @@
 				<bs3:tab name="General">
 					<bs3:row>
 						<bs3:col span="12">
-							<cpt:fieldSet name="General" id="general" >
-								<cpt:property id="name"/>
-								<cpt:property id="count">
-								    <cpt:action id="incrementRedirectEvenIfSame">
-                                        <cpt:describedAs>Increment counter and redirect even if same (with repaint of entire page)</cpt:describedAs>
-                                    </cpt:action>
-								    <cpt:action id="incrementRedirectOnlyIfDiffers">
-                                        <cpt:describedAs>Increment counter and redirect only if differs (no repaint of opted-out properties)</cpt:describedAs>
-                                    </cpt:action>
-                                </cpt:property>
-								<cpt:property id="currentTime"/>
-							</cpt:fieldSet>
+                            <cpt:fieldSet name="General" id="general" >
+                                <cpt:property id="name" labelPosition="NONE"/>
+                            </cpt:fieldSet>
 						</bs3:col>
 					</bs3:row>
 				</bs3:tab>
@@ -64,6 +55,19 @@
 					</bs3:row>
 				</bs3:tab>
 			</bs3:tabGroup>
+            <cpt:fieldSet name="" id="counter" >
+                <cpt:property id="count">
+                    <cpt:action id="incrementRedirectEvenIfSame">
+                        <cpt:describedAs>Increment counter and redirect even if same (with repaint of entire page)</cpt:describedAs>
+                    </cpt:action>
+                    <cpt:action id="incrementRedirectOnlyIfDiffers">
+                        <cpt:describedAs>Increment counter and redirect only if differs (no repaint of opted-out properties)</cpt:describedAs>
+                    </cpt:action>
+                </cpt:property>
+            </cpt:fieldSet>
+            <cpt:fieldSet name="Blob" id="blob" >
+                <cpt:property id="blob"/>
+            </cpt:fieldSet>
 		</bs3:col>
 		<bs3:col span="6">
 			<cpt:fieldSet name="Description" id="description" >
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java
index 6c48819ea5..8a15eec0ab 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java
@@ -26,12 +26,11 @@ import org.apache.causeway.applib.annotation.*;
 //tag::class[]
 @Action(semantics = SemanticsOf.IDEMPOTENT)
 @ActionLayout(
-        redirectPolicy = Redirect.EVEN_IF_SAME      // <.>
+    redirectPolicy = Redirect.EVEN_IF_SAME                  // <.>
 )
 @RequiredArgsConstructor
 public class ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame {
-    // ...
-//end::class[]
+
     private final ActionLayoutRedirectPolicyEntity entity;
 
     @MemberSupport public ActionLayoutRedirectPolicyEntity act() {
@@ -39,7 +38,5 @@ public class ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame {
         entity.setCount(count == null ? 1 : count + 1);
         return entity;
     }
-
-//tag::class[]
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java
index 319fbf464f..784a2f552e 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java
@@ -26,20 +26,17 @@ import org.apache.causeway.applib.annotation.*;
 //tag::class[]
 @Action(semantics = SemanticsOf.IDEMPOTENT)
 @ActionLayout(
-        redirectPolicy = Redirect.ONLY_IF_DIFFERS       // <.>
+    redirectPolicy = Redirect.ONLY_IF_DIFFERS               // <.>
 )
 @RequiredArgsConstructor
 public class ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers {
-    // ...
-//end::class[]
+
     private final ActionLayoutRedirectPolicyEntity entity;
 
     @MemberSupport public ActionLayoutRedirectPolicyEntity act() {
         Integer count = entity.getCount();
         entity.setCount(count == null ? 1 : count + 1);
-        return entity;
+        return entity;                                      // <.>
     }
-
-//tag::class[]
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage#objects.columnOrder.txt b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage#objects.columnOrder.txt
new file mode 100644
index 0000000000..39db649d0f
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage#objects.columnOrder.txt
@@ -0,0 +1,3 @@
+name
+count
+blob
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage-description.adoc
index 353c8bcb44..466372a9fb 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage-description.adoc
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/ActionLayoutRedirectPolicyPage-description.adoc
@@ -2,60 +2,56 @@
 
 [WARNING]
 ====
-This feature is currently *not* supported by the _Wicket Viewer_; see link:https://issues.apache.org/jira/browse/CAUSEWAY-1659[CAUSEWAY-1659]
+This feature is currently *not* supported by the _Wicket Viewer_; tracked by link:https://issues.apache.org/jira/browse/CAUSEWAY-1659[CAUSEWAY-1659]
 ====
 
-If an action returns its target, then the link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/ActionLayout.html#redirectpolicy[@ActionLayout#redirectPolicy]` attribute determines
-whether to update the page or instead to redirect (forcing a re-rendering of a new page).
-Not re-rendering can provide a smoother UI experience.
+If an action returns its target, then the link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/ActionLayout.html#redirectpolicy[@ActionLayout#redirectPolicy] attribute determines
+whether to update the page or instead to redirect (which redraws the enter page).
+_This only has an effect on actions that return the object on which they are invoked, ie which return ``this``_.
 
+The annotation is intended to be used in combination with link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/PropertyLayout.html#repainting[@PropertyLayout#repainting]`, which specifies that a property is immutable, expensive and should not be re-rendered if necessary.
+The canonical use case is a blob property that holds a PDF, and which uses the link:https://causeway.apache.org/vw/2.0.0-RC1/pdfjs/about.html[pdf.js] viewer extension to render the PDF.
+Such a PDF is immutable.
+In such cases, not re-rendering that PDF provides a smoother UI experience.
 
 == How this demo works
 
-TODO
-
-// This page has a `count` property with two associated actions.
-// These both increment the `count`, but use different redirect policies.
-//
-// The page also has a `currentTime` property, that displays the current time.
-// This is a derived property that also has a built-in delay of 1 second.
-//
-// [source,java,indent=0]
-// ----
-// include::ActionLayoutRedirectPolicyPage.java[tags=class]
-// ----
-// <.> for demonstration purposes, this UI component opts out of being redrawn if there is no redirect.
-//
-// [NOTE]
-// ====
-// In this demo we are somewhat misusing the `@PropertyLayout#repainting` attribute; normally this is used for components that are expensive to render _and_ which are generally unchanging.
-// The most common example of such a property would be a PDF, rendered using the link:https://causeway.apache.org/vw/2.0.0-RC1/pdfjs/about.html[PDF.js] extension (the link:https://causeway.apache.org/refguide/2.0.0-RC1/extensions/index/pdfjs/applib/annotations/PdfJsViewer.html#section-top[@PdfJsViewer] annotation).
-// ====
-//
-// The behaviour observed therefore depends on which action is used:
-//
-// * if the "increment even if same" action is called:
-// +
-// [source,java,indent=0]
-// ----
-// include::ActionLayoutRedirectPolicyPage_incrementRedirectEvenIfSame.java[tags=class]
-// ----
-// <.> always render the page.
-// +
-// This causes the entire page to be re-rendered.
-// That means that the current time will be updated, but only after a 1 second delay
-//
-// * if the "increment redirect only if differs" action is called:
-// +
-// [source,java,indent=0]
-// ----
-// include::ActionLayoutRedirectPolicyPage_incrementRedirectOnlyIfDiffers.java[tags=class]
-// ----
-// <.> only re-render the updated property.
-// +
-// This _should_ cause only the updated property to re-render.
-// +
-// [WARNING]
-// ====
-// Because this feature is currently *not* supported by the _Wicket Viewer_, in fact the `currentTime` property also re-renders; see link:https://issues.apache.org/jira/browse/CAUSEWAY-1659[CAUSEWAY-1659]
-// ====
+On the left hand side is a collection of entities, each of which has a counter and a PDF property.
+The value of the PDF property is immutable and so is annotated with `@PropertyLayout#repainting` to disable repainting if possible.
+
+The entity also has two actions that update a counter, and which both return `this`.
+One of these is annotated with `@ActionLayout#redirectPolicy` to specify that the page should be updated; this should result in the PDF not being unnecessarily redrawn.
+
+[WARNING]
+====
+This feature is currently *not* supported by the _Wicket Viewer_; tracked by link:https://issues.apache.org/jira/browse/CAUSEWAY-1659[CAUSEWAY-1659]
+====
+
+In terms of code:
+
+* PDF blob
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity.java[tags=class]
+----
+<.> render the blob as a PDF
+<.> don't repaint the PDF if possible
+
+* redirect even if the same
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity_incrementRedirectOnlyIfDiffers.java[tags=class]
+----
+<.> suppress redirect if returning the same object
+<.> return the same object
+
+* redirect even if the same
++
+[source,java,indent=0]
+----
+include::ActionLayoutRedirectPolicyEntity_incrementRedirectEvenIfSame.java[tags=class]
+----
+<.> redirect _even if_ returning the same object
+
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl-description.adoc
new file mode 100644
index 0000000000..2fe08951a0
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl-description.adoc
@@ -0,0 +1,3 @@
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 ag [...]
+
+include::../ActionLayoutRedirectPolicyEntity-description.adoc[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl.java
similarity index 70%
copy from examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java
copy to examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl.java
index 110c9030cd..43da771593 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImpl.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.domain.actions.ActionLayout.redirectPolicy.jpa;
+package demoapp.dom.domain.actions.ActionLayout.redirectPolicy.jdo;
 
 import demoapp.dom.domain.actions.ActionLayout.redirectPolicy.ActionLayoutRedirectPolicyEntity;
 
@@ -24,33 +24,29 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 
-import lombok.SneakyThrows;
-
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.Nature;
 import org.apache.causeway.applib.annotation.Optionality;
 import org.apache.causeway.applib.annotation.Property;
-import org.apache.causeway.applib.annotation.PropertyLayout;
-import org.apache.causeway.applib.annotation.Repainting;
-import org.apache.causeway.applib.services.clock.ClockService;
+import org.apache.causeway.applib.value.Blob;
 import org.apache.causeway.persistence.jpa.applib.integration.CausewayEntityListener;
+import org.apache.causeway.persistence.jpa.applib.types.BlobJpaEmbeddable;
 
 import org.springframework.context.annotation.Profile;
 
-import javax.inject.Inject;
 import javax.inject.Named;
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.DatastoreIdentity;
+import javax.jdo.annotations.IdGeneratorStrategy;
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
 import javax.persistence.*;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlTransient;
 
-@Profile("demo-jpa")
-@Entity
-@Table(
-    schema = "demo",
-    name = "ActionLayoutRedirectPolicyEntity"
-)
-@EntityListeners(CausewayEntityListener.class)
+@Profile("demo-jdo")
 @Named("demo.ActionLayoutRedirectPolicyEntity")
+@PersistenceCapable(identityType = IdentityType.DATASTORE, schema = "demo")
+@DatastoreIdentity(strategy = IdGeneratorStrategy.IDENTITY, column = "id")
 @NoArgsConstructor
 //tag::class[]
 // ...
@@ -63,18 +59,20 @@ public class ActionLayoutRedirectPolicyEntityImpl extends ActionLayoutRedirectPo
         setName(value);
     }
 
-    @Id
-    @GeneratedValue
-    private Long id;
-
     @Getter @Setter
     private String name;
 
     @Property(optionality = Optionality.OPTIONAL)
-    @XmlElement(required = false)
     @Getter @Setter
     private Integer count;
 
+    @Persistent(defaultFetchGroup="false", columns = {
+            @javax.jdo.annotations.Column(name = "blob_name"),
+            @javax.jdo.annotations.Column(name = "blob_mimetype"),
+            @Column(name = "blob_bytes")
+    })
+    @Getter @Setter
+    private Blob blob;
 //tag::class[]
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImplRepository.java
similarity index 64%
copy from examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java
copy to examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImplRepository.java
index 4e9deefc9a..1dc20b068c 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jdo/ActionLayoutRedirectPolicyEntityImplRepository.java
@@ -16,26 +16,52 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.domain.actions.ActionLayout.redirectPolicy.jpa;
+package demoapp.dom.domain.actions.ActionLayout.redirectPolicy.jdo;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
 
 import org.springframework.context.annotation.Profile;
 import org.springframework.stereotype.Service;
 
+import org.apache.causeway.applib.value.Blob;
+import org.apache.causeway.applib.value.NamedWithMimeType;
+
 import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.domain.actions.ActionLayout.redirectPolicy.ActionLayoutRedirectPolicyEntityRepository;
+import demoapp.dom.types.Samples;
+
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-@Profile("demo-jpa")
+@Profile("demo-jdo")
 @Service
 public class ActionLayoutRedirectPolicyEntityImplRepository
 extends ValueHolderRepository<String, ActionLayoutRedirectPolicyEntityImpl> implements ActionLayoutRedirectPolicyEntityRepository {
 
+    @Inject Samples<Blob> blobSamples;
+
+    final Random random;
+
     protected ActionLayoutRedirectPolicyEntityImplRepository() {
         super(ActionLayoutRedirectPolicyEntityImpl.class);
+        random = new Random(DateTime.now().getMillis());
     }
 
     @Override
     protected ActionLayoutRedirectPolicyEntityImpl newDetachedEntity(String value) {
-        return new ActionLayoutRedirectPolicyEntityImpl(value);
+        var entity = new ActionLayoutRedirectPolicyEntityImpl(value);
+        entity.setBlob(randomBlob());
+        return entity;
     }
 
+    private Blob randomBlob() {
+        List<Blob> list = blobSamples.stream()
+                    .filter(x -> NamedWithMimeType.CommonMimeType.PDF.matches(x.getMimeType()))
+                    .collect(Collectors.toList());
+        return list.get(random.nextInt(list.size()));
+    }
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java
index 110c9030cd..19ac1efaf1 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImpl.java
@@ -24,24 +24,19 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 
-import lombok.SneakyThrows;
-
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.Nature;
 import org.apache.causeway.applib.annotation.Optionality;
 import org.apache.causeway.applib.annotation.Property;
-import org.apache.causeway.applib.annotation.PropertyLayout;
-import org.apache.causeway.applib.annotation.Repainting;
-import org.apache.causeway.applib.services.clock.ClockService;
+import org.apache.causeway.applib.value.Blob;
 import org.apache.causeway.persistence.jpa.applib.integration.CausewayEntityListener;
 
+import org.apache.causeway.persistence.jpa.applib.types.BlobJpaEmbeddable;
+
 import org.springframework.context.annotation.Profile;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 import javax.persistence.*;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlTransient;
 
 @Profile("demo-jpa")
 @Entity
@@ -71,10 +66,27 @@ public class ActionLayoutRedirectPolicyEntityImpl extends ActionLayoutRedirectPo
     private String name;
 
     @Property(optionality = Optionality.OPTIONAL)
-    @XmlElement(required = false)
     @Getter @Setter
     private Integer count;
 
+    @AttributeOverrides({
+            @AttributeOverride(name="name",    column=@Column(name="blob_name")),
+            @AttributeOverride(name="mimeType",column=@Column(name="blob_mimeType")),
+            @AttributeOverride(name="bytes",   column=@Column(name="blob_bytes"))
+    })
+    @Embedded
+    private BlobJpaEmbeddable blobJpaEmbeddable;
+
+    @Override
+    public Blob getBlob() {
+        return BlobJpaEmbeddable.toBlob(blobJpaEmbeddable);
+    }
+    @Override
+    public void setBlob(final Blob blob) {
+        this.blobJpaEmbeddable = BlobJpaEmbeddable.fromBlob(blob);
+    }
+
+
 //tag::class[]
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java
index 4e9deefc9a..bcb1d29fa4 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/actions/ActionLayout/redirectPolicy/jpa/ActionLayoutRedirectPolicyEntityImplRepository.java
@@ -18,24 +18,43 @@
  */
 package demoapp.dom.domain.actions.ActionLayout.redirectPolicy.jpa;
 
+import demoapp.dom.types.Samples;
+
+import lombok.RequiredArgsConstructor;
+
+import org.apache.causeway.applib.value.Blob;
+import org.apache.causeway.applib.value.NamedWithMimeType;
+
 import org.springframework.context.annotation.Profile;
 import org.springframework.stereotype.Service;
 
 import demoapp.dom._infra.values.ValueHolderRepository;
 import demoapp.dom.domain.actions.ActionLayout.redirectPolicy.ActionLayoutRedirectPolicyEntityRepository;
 
+import javax.inject.Inject;
+
 @Profile("demo-jpa")
 @Service
 public class ActionLayoutRedirectPolicyEntityImplRepository
 extends ValueHolderRepository<String, ActionLayoutRedirectPolicyEntityImpl> implements ActionLayoutRedirectPolicyEntityRepository {
 
+    @Inject Samples<Blob> blobSamples;
+
     protected ActionLayoutRedirectPolicyEntityImplRepository() {
         super(ActionLayoutRedirectPolicyEntityImpl.class);
     }
 
     @Override
     protected ActionLayoutRedirectPolicyEntityImpl newDetachedEntity(String value) {
-        return new ActionLayoutRedirectPolicyEntityImpl(value);
+        var entity = new ActionLayoutRedirectPolicyEntityImpl(value);
+        blobSamples.stream()
+                .filter(x -> NamedWithMimeType.CommonMimeType.PDF.matches(x.getMimeType()))
+                .findFirst()
+                .ifPresent(pdfBlob -> {
+                    entity.setBlob(pdfBlob);
+                });
+        return entity;
     }
 
+
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/PropertyLayoutMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/PropertyLayoutMenu.java
index 906f4ca881..0d8b16e3f8 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/PropertyLayoutMenu.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/PropertyLayoutMenu.java
@@ -53,8 +53,6 @@ import demoapp.dom.types.Samples;
 //@Log4j2
 public class PropertyLayoutMenu {
 
-    final Samples<Blob> samples;
-
 
     @Action(semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa="fa-pen-nib", describedAs = "CSS class to wrap the UI component representing this property")
@@ -132,15 +130,8 @@ public class PropertyLayoutMenu {
     @Action(semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa="fa-paint-brush", describedAs = "Performance hint for properties holding unchanging large objects")
     public PropertyLayoutRepaintingPage repainting(){
-        val vm = new PropertyLayoutRepaintingPage();
-        vm.setEditMe("Modify this field to see if repainting occurs...");
-        samples.stream()
-                .filter(x -> CommonMimeType.PDF.matches(x.getMimeType()))
-                .findFirst()
-                .ifPresent(pdfBlob -> {
-                    vm.setBlob(pdfBlob);
-                });
-        return vm;
+        val page = new PropertyLayoutRepaintingPage();
+        return page;
     }
 
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage-description.adoc
index 5ef1fa2c7c..5e012d8dee 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage-description.adoc
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage-description.adoc
@@ -2,37 +2,12 @@
 
 WARNING: this functionality is currently *not* supported by the Wicket viewer; see link:https://issues.apache.org/jira/browse/CAUSEWAY-1659[CAUSEWAY-1659].
 
-The `repainting` attribute indicates that the value held by the property never changes over time, even when other properties of the object do change.
+The link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/PropertyLayout.html#repainting[@PropertyLayout#repainting] attribute indicates that the value held by the property never changes over time, even when other properties of the object do change.
 When set to "no repaint", the viewer uses this element to not repaint the property after an AJAX update even if some other property/ies of the object have changed.
 
-* for properties, the attribute can be specified using either the link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/PropertyLayout.html#repainting[@PropertyLayout#repainting] annotation, or using the link:https://causeway.apache.org/userguide/2.0.0-RC1/fun/ui.html#by-example[.layout.xml] layout file.
+For this attribute to work, the action being invoked must return the same object (return `this`) and must also be annotated with link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/ActionLayout.html#redirectpolicy[@ActionLayout#redirectPolicy].
 
-The attribute does _not_ apply for action parameters.
-
-The primary use case for performance, for example so that large PDFs/blobs do not need to be re-rendered.
-
-IMPORTANT: if there are multiple blobs or clobs, then _all_ must specify "NO REPAINT".
-Said another way: if only one blob/clob property requires a repaint, then all will be repainted.
 
 == How this demo works
 
-This page contains two properties.
-
-* The `editMe` property is to demonstrate a change in state can be made.
-
-* The `blob` property holds a PDF and moreover is annotated with link:https://causeway.apache.org/refguide/2.0.0-RC1/extensions/index/pdfjs/applib/annotations/PdfJsViewer.html[@PdfJsViewer] so that it is rendered using PDF.js.
-
-From a usability perspective, we don't want the page to redraw - in particluar, the `blob` property - if all that has changed is the `editMe` property.
-
-The (no) repainting hint is specified explicitly using `@PropertyLayout#repainting()`:
-
-[source,java,indent=0]
-----
-include::PropertyLayoutRepaintingPage.java[tags=annotation]
-----
-<.> explicitly specifies that the property should not be repainted
-<.> The Wicket pdf.js extension will render this Blob as a PDF
-
-TIP: this annotation can also be combined with link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/annotation/ActionLayout.html#redirectpolicy[@ActionLayout#redirectPolicy].
-
-
+See the demo page for `@ActionLayout > Redirect Policy` for a full demo of how to use the `@PropertyLayout#repainting` in conjunction with `@ActionLayout#redirectPolicy`.
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.java
index ec71271178..a0a01b3381 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.java
@@ -53,25 +53,5 @@ public class PropertyLayoutRepaintingPage implements HasAsciiDocDescription {
         return "@PropertyLayout#repainting";
     }
 
-    @Property(editing = Editing.ENABLED, optionality = Optionality.OPTIONAL)
-    @PropertyLayout(
-        describedAs =
-            "Editable property (PDF should not repaint if it changes)"
-    )
-    @XmlElement(required = false)
-    @Getter @Setter
-    private String editMe;
-
-//tag::annotation[]
-    @Property()
-    @PropertyLayout(
-        repainting = Repainting.NO_REPAINT      // <.>
-    )
-    @XmlElement(required = true)
-    @PdfJsViewer                                // <.>
-    @Getter @Setter
-    private Blob blob;
-//end::annotation[]
-
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml
index 652f933f51..cf01fbcb0b 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml
@@ -26,21 +26,7 @@
 
 	<bs3:row>
 		<bs3:col span="6">
-			<cpt:fieldSet name="Edit" id="edit" >
-                <cpt:property id="editMe"/>
-            </cpt:fieldSet>
-			<cpt:fieldSet name="Annotated" id="annotation">
-                <cpt:property id="blob"/>
-            </cpt:fieldSet>
-			<cpt:fieldSet name="Layout File" id="layout-file">
-<!-- tag::layout-file[] -->
-				<cpt:property id="propertyUsingLayout" repainting="NO_REPAINT"/>
-<!-- end::layout-file[] -->
-			</cpt:fieldSet>
-			<cpt:fieldSet name="Contributed" id="contributed"/>
-			<cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
-			<cpt:fieldSet name="Meta-annotated Overridden" id="meta-annotated-overridden"/>
-			<cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
+			<cpt:fieldSet name="" id="unreferenced" unreferencedProperties="true"/>
 		</bs3:col>
 		<bs3:col span="6">
 			<cpt:fieldSet name="Description" id="description" >
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/ValueSemanticsMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/ValueSemanticsMenu.java
index f087b50938..bc586e905a 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/ValueSemanticsMenu.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/ValueSemanticsMenu.java
@@ -18,11 +18,14 @@
  */
 package demoapp.dom.domain.properties.ValueSemantics;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import demoapp.dom.domain.properties.ValueSemantics.percentage.ValueSemanticsProviderPercentagePage;
+
 import org.apache.causeway.applib.annotation.Action;
 import org.apache.causeway.applib.annotation.ActionLayout;
 import org.apache.causeway.applib.annotation.DomainService;
@@ -56,4 +59,14 @@ public class ValueSemanticsMenu {
     }
 //end::create-page[]
 
+//tag::percentage[]
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(cssClassFa="fa-step-forward", describedAs = "Use a custom ValueSemanticsProvider with meta-annotation to customise the display")
+    public ValueSemanticsProviderPercentagePage percentage(){
+        val page = new ValueSemanticsProviderPercentagePage();
+        page.setPercentage(new BigDecimal(".95"));
+        return page;
+    }
+//end::percentage[]
+
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/dateRenderAdjustDays/ValueSemanticsDateRenderAdjustDaysPage.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/dateRenderAdjustDays/ValueSemanticsDateRenderAdjustDaysPage.java
index 03d4f27fc9..4254828918 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/dateRenderAdjustDays/ValueSemanticsDateRenderAdjustDaysPage.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/dateRenderAdjustDays/ValueSemanticsDateRenderAdjustDaysPage.java
@@ -50,7 +50,7 @@ import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
 @XmlRootElement(name = "root")
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
-@Named("demo.PropertyLayoutRenderDayVm")
+@Named("demo.ValueSemanticsDateRenderAdjustDaysPage")
 @DomainObject(
         nature=Nature.VIEW_MODEL,
         editing = Editing.ENABLED
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/Percentage.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/Percentage.java
new file mode 100644
index 0000000000..528f49811c
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/Percentage.java
@@ -0,0 +1,19 @@
+package demoapp.dom.domain.properties.ValueSemantics.percentage;
+
+
+import org.apache.causeway.applib.annotation.ValueSemantics;
+
+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;
+
+@ValueSemantics(provider = "percentage")
+@Inherited
+@Target({
+        ElementType.METHOD, ElementType.FIELD,
+        ElementType.PARAMETER,
+})
+@Retention(RetentionPolicy.RUNTIME)public @interface Percentage {
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderForPercentage.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderForPercentage.java
new file mode 100644
index 0000000000..0ef8cba396
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderForPercentage.java
@@ -0,0 +1,26 @@
+package demoapp.dom.domain.properties.ValueSemantics.percentage;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import javax.annotation.Priority;
+import javax.inject.Named;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.value.semantics.ValueSemanticsProvider;
+import org.apache.causeway.core.metamodel.valuesemantics.BigDecimalValueSemantics;
+
+@Component
+@Named("demo.ValueSemanticsProviderForPercentage")
+@Qualifier("percentage")
+@Priority(PriorityPrecedence.MIDPOINT)
+public class ValueSemanticsProviderForPercentage extends BigDecimalValueSemantics {
+
+        @Override
+        public String htmlPresentation(final ValueSemanticsProvider.Context context, final BigDecimal value) {
+            return value.multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_EVEN) + "%";
+        }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage-description.adoc
new file mode 100644
index 0000000000..c0c8ea1329
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage-description.adoc
@@ -0,0 +1,7 @@
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 ag [...]
+
+WARNING: TODO
+
+== How this demo works
+
+WARNING: TODO
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.java
new file mode 100644
index 0000000000..2b22aac1b4
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 demoapp.dom.domain.properties.ValueSemantics.percentage;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import org.apache.causeway.applib.annotation.*;
+import org.apache.causeway.applib.jaxb.JavaTimeJaxbAdapters;
+
+import javax.inject.Named;
+import javax.xml.bind.annotation.*;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+//tag::class[]
+@XmlRootElement(name = "root")
+@XmlType
+@XmlAccessorType(XmlAccessType.FIELD)
+@Named("demo.ValueSemanticsProviderPercentagePage")
+@DomainObject(
+        nature=Nature.VIEW_MODEL,
+        editing = Editing.ENABLED
+)
+@NoArgsConstructor
+public class ValueSemanticsProviderPercentagePage implements HasAsciiDocDescription {
+
+    @ObjectSupport public String title() {
+        return "@ValueSemanticsProvider for percentage";
+    }
+
+    @Percentage
+    @XmlElement(required = false)
+    @Property(optionality = Optionality.OPTIONAL)
+    @Getter @Setter
+    private BigDecimal percentage;
+
+}
+//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.layout.xml
similarity index 82%
copy from examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml
copy to examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.layout.xml
index 652f933f51..41966cc762 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/PropertyLayout/repainting/PropertyLayoutRepaintingPage.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/properties/ValueSemantics/percentage/ValueSemanticsProviderPercentagePage.layout.xml
@@ -18,6 +18,7 @@
 	<bs3:row>
 		<bs3:col span="10" unreferencedActions="true">
 			<cpt:domainObject />
+			<cpt:action id="downloadAsXml"/>
 		</bs3:col>
 		<bs3:col span="2">
 			<cpt:fieldSet name="" id="sources" />
@@ -26,20 +27,13 @@
 
 	<bs3:row>
 		<bs3:col span="6">
-			<cpt:fieldSet name="Edit" id="edit" >
-                <cpt:property id="editMe"/>
+			<cpt:fieldSet name="General" id="general">
+                <cpt:property id="startDate"/>
+                <cpt:property id="endDate"/>
             </cpt:fieldSet>
-			<cpt:fieldSet name="Annotated" id="annotation">
-                <cpt:property id="blob"/>
+			<cpt:fieldSet name="Internal" id="internal">
+                <cpt:property id="endDateNotAdjusted"/>
             </cpt:fieldSet>
-			<cpt:fieldSet name="Layout File" id="layout-file">
-<!-- tag::layout-file[] -->
-				<cpt:property id="propertyUsingLayout" repainting="NO_REPAINT"/>
-<!-- end::layout-file[] -->
-			</cpt:fieldSet>
-			<cpt:fieldSet name="Contributed" id="contributed"/>
-			<cpt:fieldSet name="Meta-annotated" id="meta-annotated"/>
-			<cpt:fieldSet name="Meta-annotated Overridden" id="meta-annotated-overridden"/>
 			<cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
 		</bs3:col>
 		<bs3:col span="6">
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index aba11689ea..8432e517af 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -109,6 +109,7 @@ For latest we use: https://raw.githubusercontent.com/apache/causeway/master/anto
             <mb3:section>
                 <mb3:named>@ValueSemantics</mb3:named>
                 <mb3:serviceAction objectType="demo.ValueSemanticsMenu" id="dateRenderAdjustDays"/>
+                <mb3:serviceAction objectType="demo.ValueSemanticsMenu" id="percentage"/>
             </mb3:section>
         </mb3:menu>