You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2020/03/17 00:08:29 UTC

[isis] 27/42: ISIS-2062: factors out XmlService from XmlSnapshotService, reintroduces TransactionService#executeInTransaction(...)

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

danhaywood pushed a commit to branch ISIS-2278
in repository https://gitbox.apache.org/repos/asf/isis.git

commit f9c9f74f74d9649ca4e055f6447fd0fe54213b8a
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Mar 13 06:15:16 2020 +0100

    ISIS-2062: factors out XmlService from XmlSnapshotService, reintroduces TransactionService#executeInTransaction(...)
---
 .../services/xactn/TransactionService.java         |  43 ++++----
 .../XmlService.java}                               |  55 +++--------
 .../examples/services/xml/package-info.java        |  24 +++++
 .../services/xmlsnapshot/XmlSnapshotService.java   |  73 +++-----------
 .../applib-svc/pages/TransactionService.adoc       |   4 +
 .../adoc/modules/applib-svc/pages/XmlService.adoc  |  29 ++++++
 .../applib-svc/pages/XmlSnapshotService.adoc       | 100 +++++++------------
 .../modules/applib-svc/partials/module-nav.adoc    |   1 +
 .../applib/services/xactn/TransactionService.java  |  43 ++++----
 .../isis/applib/services/xml/XmlService.java}      |  55 +++--------
 .../isis/applib/services/xml/package-info.java     |  24 +++++
 .../services/xmlsnapshot/XmlSnapshotService.java   |  73 +++-----------
 .../xmlsnapshot/XmlSnapshotServiceAbstract.java    | 110 +--------------------
 .../core/metamodel/util/snapshot/XmlSnapshot.java  |  33 -------
 .../IsisModuleCoreRuntimeServices.java             |   8 +-
 .../repository/RepositoryServiceDefault.java       |  10 ++
 .../routing/RoutingServiceDefault.java             |  10 ++
 .../scratchpad/ScratchpadDefault.java              |  12 +--
 .../xactn/TransactionServiceSpring.java            |  96 +++++++++---------
 .../runtimeservices/xml/XmlServiceDefault.java     | 104 +++++++++----------
 .../xmlsnapshot/XmlSnapshotServiceDefault.java     |  87 ++++++++++++++--
 .../xmlsnapshot/XmlSnapshotServiceAbstractTest.xml |   0
 .../XmlSnapshotServiceDefault_Test.java            |  31 +++---
 23 files changed, 430 insertions(+), 595 deletions(-)

diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
index 578d883..fb5d805 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
@@ -66,26 +66,30 @@ public interface TransactionService {
     // tag::refguide[]
     void nextTransaction();                                 // <.>
 
-//    // end::refguide[]
-//
+    // end::refguide[]
+    /**
+     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
+     * boundary creates a new one.
+     *
+     * @param task
+     */
+    // tag::refguide[]
+    void executeWithinTransaction(Runnable task);           // <.>
+
+    // end::refguide[]
+    /**
+     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
+     * boundary creates a new one.
+     *
+     * @param task
+     */
+    // tag::refguide[]
+    <T> T executeWithinTransaction(Supplier<T> task);       // <.>
+
+    // end::refguide[]
+
+//   the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
 //   not sure there's any need for these additional methods?
-//   in any case, the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
-//
-//    /**
-//     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
-//     * boundary creates a new one.
-//     *
-//     * @param task
-//     */
-//    void executeWithinTransaction(Runnable task);           // <.>
-//
-//    /**
-//     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
-//     * boundary creates a new one.
-//     *
-//     * @param task
-//     */
-//    <T> T executeWithinTransaction(Supplier<T> task);       // <.>
 //
 //    /**
 //     * Runs given {@code task} within its own (new) transactional boundary.
@@ -100,6 +104,5 @@ public interface TransactionService {
 //    <T> T executeWithinNewTransaction(Supplier<T> task);    // <.>
 
     // tag::refguide[]
-
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/XmlService.java
similarity index 51%
copy from api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
copy to api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/XmlService.java
index 07c399d..7081689 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/XmlService.java
@@ -16,43 +16,18 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.xmlsnapshot;
+package org.apache.isis.applib.services.xml;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService;
 
 /**
- * This service allows an XML document to be generated capturing the data of a root entity and specified related
- * entities.  This XML can be used for various purposes, such as mail merge/reporting, or adhoc auditing.
- *
- * <p>
- * The framework provides an implementation of this service (<tt>XmlSnapshotServiceDefault</tt>) which is automatically
- * registered and available for use; no further configuration is required.
- * </p>
+ * This service provides a number of utility services for interacting with XML {@link Document}s
  */
 // tag::refguide[]
-public interface XmlSnapshotService {
-
-    // end::refguide[]
-    // tag::refguide-1[]
-    interface Snapshot {
-        Document getXmlDocument();
-        Document getXsdDocument();
-
-        String getXmlDocumentAsString();
-        String getXsdDocumentAsString();
-    }
-    // end::refguide-1[]
-
-    // tag::refguide-2[]
-    interface Builder {
-        void includePath(final String path);
-        void includePathAndAnnotation(final String path, final String annotation);
-        XmlSnapshotService.Snapshot build();
-    }
-    // end::refguide-2[]
+public interface XmlService {
 
     // tag::refguide-3[]
     class Exception extends RuntimeException {
@@ -77,29 +52,25 @@ public interface XmlSnapshotService {
     }
     // end::refguide-3[]
 
-    // tag::refguide[]
-    XmlSnapshotService.Snapshot snapshotFor(                    // <.>
-                                    final Object domainObject);
-
-    XmlSnapshotService.Builder builderFor(                      // <.>
-                                    final Object domainObject);
 
     // end::refguide[]
     /**
-     * Convenience to convert xml string (eg as obtained by {@link Snapshot#getXmlDocumentAsString()} or
-     * {@link Snapshot#getXsdDocumentAsString()}) back into a {@link Document W3C Document}.
+     * Converts xml string into an {@link Document W3C Document}.
+     *
+     * @see #asString(Document)
      */
     // tag::refguide[]
     Document asDocument(String xmlStr);                         // <.>
 
     // end::refguide[]
+
     /**
-     * Convenience method to extract value of an XML element, based on its type.
+     * Serializes a {@link Document W3C Document} back into a string.
+     *
+     * @see #asDocument(String)
      */
     // tag::refguide[]
-    <T> T getChildElementValue(                                 // <.>
-            final Element el, final String tagname,
-            final Class<T> expectedCls);
+    public String asString(final Document doc);                 // <.>
 
     // end::refguide[]
     /**
@@ -107,7 +78,7 @@ public interface XmlSnapshotService {
      */
     // tag::refguide[]
     Element getChildElement(                                    // <.>
-            final Element el, final String tagname);
+                    final Element el, final String tagname);
 
     // end::refguide[]
     /**
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/package-info.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/package-info.java
new file mode 100644
index 0000000..3bc3341
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xml/package-info.java
@@ -0,0 +1,24 @@
+/*
+ *  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.
+ */
+
+/**
+ * The {@link org.apache.isis.applib.services.xml.XmlService} provides utilities for working with
+ * {@link org.w3c.dom.Document w3c DOM Document}s.
+ */
+package org.apache.isis.applib.services.xml;
\ No newline at end of file
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
index 07c399d..a1d1388 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
@@ -21,8 +21,6 @@ package org.apache.isis.applib.services.xmlsnapshot;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.isis.applib.annotation.Programmatic;
-
 /**
  * This service allows an XML document to be generated capturing the data of a root entity and specified related
  * entities.  This XML can be used for various purposes, such as mail merge/reporting, or adhoc auditing.
@@ -38,61 +36,30 @@ public interface XmlSnapshotService {
     // end::refguide[]
     // tag::refguide-1[]
     interface Snapshot {
-        Document getXmlDocument();
-        Document getXsdDocument();
-
-        String getXmlDocumentAsString();
-        String getXsdDocumentAsString();
-    }
-    // end::refguide-1[]
-
-    // tag::refguide-2[]
-    interface Builder {
-        void includePath(final String path);
-        void includePathAndAnnotation(final String path, final String annotation);
-        XmlSnapshotService.Snapshot build();
-    }
-    // end::refguide-2[]
-
-    // tag::refguide-3[]
-    class Exception extends RuntimeException {
-
-        private static final long serialVersionUID = 1L;
-
-        public Exception() {
-            super();
-        }
-
-        public Exception(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        public Exception(String message) {
-            super(message);
-        }
-
-        public Exception(Throwable cause) {
-            super(cause);
+        Document getXmlDocument();          // <.>
+        Document getXsdDocument();          // <.>
+        // end::refguide-1[]
+        // tag::refguide-2[]
+        interface Builder {
+            void includePath(final String path);                    // <.>
+            void includePathAndAnnotation(                          // <.>
+                final String path, final String annotation);
+            XmlSnapshotService.Snapshot build();                    // <.>
         }
+        // end::refguide-2[]
+        // tag::refguide-1[]
     }
-    // end::refguide-3[]
+    // end::refguide-1[]
 
     // tag::refguide[]
     XmlSnapshotService.Snapshot snapshotFor(                    // <.>
                                     final Object domainObject);
 
-    XmlSnapshotService.Builder builderFor(                      // <.>
+    XmlSnapshotService.Snapshot.Builder builderFor(             // <.>
                                     final Object domainObject);
 
     // end::refguide[]
-    /**
-     * Convenience to convert xml string (eg as obtained by {@link Snapshot#getXmlDocumentAsString()} or
-     * {@link Snapshot#getXsdDocumentAsString()}) back into a {@link Document W3C Document}.
-     */
-    // tag::refguide[]
-    Document asDocument(String xmlStr);                         // <.>
 
-    // end::refguide[]
     /**
      * Convenience method to extract value of an XML element, based on its type.
      */
@@ -101,20 +68,6 @@ public interface XmlSnapshotService {
             final Element el, final String tagname,
             final Class<T> expectedCls);
 
-    // end::refguide[]
-    /**
-     * Convenience method to walk XML document.
-     */
-    // tag::refguide[]
-    Element getChildElement(                                    // <.>
-            final Element el, final String tagname);
-
-    // end::refguide[]
-    /**
-     * Convenience method to obtain value of child text node.
-     */
-    // tag::refguide[]
-    String getChildTextValue(final Element el);                 // <.>
 
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/TransactionService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/TransactionService.adoc
index 362c243..93f8e2b 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/TransactionService.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/TransactionService.adoc
@@ -46,6 +46,10 @@ Performing a flush will cause any pending calls to be performed immediately.
 <.> The framework automatically start a transaction before each user interaction (action invocation or property edit), and will commit that transaction after the interaction has completed.
 Under certain circumstances (eg actions used to perform data migration, say, or for large fixture scripts), it can be helpful to programmatically complete one transaction and start another one.
 
+<.> Primarily for internal use, will execute the task within the current transaction if there is one, but if there is none then begin and then commit a new transaction for the task.
+
+<.> Ditto, but retrieving the task to be performed from the supplier.
+
 
 == Implementation
 
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/XmlService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/XmlService.adoc
new file mode 100644
index 0000000..536d3f8
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/XmlService.adoc
@@ -0,0 +1,29 @@
+= `XmlService`
+
+: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 [...]
+:page-partial:
+
+
+The `XmlService` provides general purpose utilities for working with w3c DOM ``Document``s.
+It is used by the framework's default implementation of xref:refguide:applib-svc:XmlSnapshotService.adoc[`XmlSnapshotService`].
+
+
+== API
+
+The API of `XmlService` is:
+
+[source]
+----
+include::refguide:applib-svc:example$services/xmlsnapshot/XmlService.java[tags="refguide"]
+----
+<1> is a convenience method to convert xml string back into a W3C Document
+<2> is a convenience method to extract the value of an XML element, based on its type.
+<3> is a convenience method to walk XML document.
+<4> is a convenience method to obtain value of child text node.
+
+
+
+== Implementation
+
+The framework provides a default implementation of this service, `o.a.i.core.runtimeservices.xml.XmlServiceDefault`.
+
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/XmlSnapshotService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/XmlSnapshotService.adoc
index 4cfe129..e7aa076 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/XmlSnapshotService.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/XmlSnapshotService.adoc
@@ -3,20 +3,16 @@
 :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 [...]
 :page-partial:
 
-WARNING: TODO - to review for v2.
-
 
 The `XmlSnapshotService` provides the capability to generate XML snapshots (and if required corresponding XSD schemas) based on graphs of domain objects.
 
-Typical use cases include creating mementos for business-focused auditing, such that a report could be generated as to which end-user performed a business action (perhaps for legal reasons).  For one system that we know of, a digest of this snapshot of data is signed with the public encryption key so as to enforce non-repudiation.
+Typical use cases include creating mementos for business-focused auditing, such that a report could be generated as to which end-user performed a business action (perhaps for legal reasons).
+For one system that we know of, a digest of this snapshot of data is signed with the public encryption key so as to enforce non-repudiation.
 
 Another use case is to grab raw data such that it could be merged into a report template or communication.
 
 The service offers a basic API to create a snapshot of a single object, and an more flexible API that allows the size of the graph to be customized.
 
-
-
-
 == Standard API
 
 The (basic) API of `XmlSnapshotService` is:
@@ -26,17 +22,36 @@ The (basic) API of `XmlSnapshotService` is:
 include::refguide:applib-svc:example$services/xmlsnapshot/XmlSnapshotService.java[tags="refguide"]
 ----
 
+<.> exports the state of a domain object into a `Snapshot` (which can then be converted into XML, for example).
+<.> creates a `Snapshot.Builder` that allows the contents of the snapshot to include other related state.
+<.> Convenience method to extract the value of an XML element, based on its type attribute (`isis:xxx`).
+
+where `Snapshot` is:
+
+[source]
+----
+include::refguide:applib-svc:example$services/xmlsnapshot/XmlSnapshotService.java[tags="refguide-1",indent=0]
+----
+
+<.> Converts the snapshotted state into an XML document.
+<.> Creates a corresponding XSD that describes the structure of the exported XML.
+
+and `Snapshot.Builder` is:
+
 [source]
 ----
-include::refguide:applib-svc:example$services/xmlsnapshot/XmlSnapshotService.java[tags="refguide-1"]
+include::refguide:applib-svc:example$services/xmlsnapshot/XmlSnapshotService.java[tags="refguide-2",indent=0]
 ----
 
+<.> Enrich the snapshot to include the state of these referenced objects <.> Ditto, but add an XML `annotation` attribute to the included element(s).
+<.> Builds the `Snapshot`.
+
+The general xref:#usage[usage] and the usage of xref:#builder-api[builder] in particular is described below.
 
 == Implementation
 
 The framework provides a default implementation of this service, `o.a.i.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefault`.
 
-
 == Usage
 
 The most straight-forward usage of this service is simply:
@@ -49,7 +64,6 @@ Element customerAsXml = snapshot.getXmlElement();
 
 This will return an XML (document) element that contains the names and values of each of the customer's value properties, along with the titles of reference properties, and also the number of items in collections.
 
-
 As well as obtaining the XML snapshot, it is also possible to obtain an XSD schema that the XML snapshot conforms to.
 
 [source]
@@ -59,16 +73,15 @@ Element customerAsXml = snapshot.getXmlElement();
 Element customerXsd = snapshot.getXsdElement();
 ----
 
-This can be useful for some tools.  For example, link:http://www.altova.com/stylevision.html[Altova Stylevision] can use the XML and XSD to transform into reports.  Please note that this link does not imply endorsement (nor even a recommendation that this is a good design).
-
-
-
-
+This can be useful for some tools.
+For example, link:http://www.altova.com/stylevision.html[Altova Stylevision] can use the XML and XSD to transform into reports.
+Please note that this link does not imply endorsement (nor even a recommendation that this is a good design).
 
 == Builder API
 
-The contents of the snapshot can be adjusted by including "paths" to other references or collections. To do this, the
-builder is used.  The API for this is:
+The contents of the snapshot can be adjusted by including "paths" to other references or collections.
+To do this, the builder is used.
+The API for this is:
 
 [source,java]
 ----
@@ -91,15 +104,15 @@ We start by obtaining a builder:
 XmlSnapshot.Builder builder = xmlsnapshotService.builderFor(customer);
 ----
 
-Suppose now that we want the snapshot to also include details of the customer's address, where `address` in this case is a reference property to an instance of the `Address` class. We can "walk-the-graph" by including these references within the builder.
+Suppose now that we want the snapshot to also include details of the customer's address, where `address` in this case is a reference property to an instance of the `Address` class.
+We can "walk-the-graph" by including these references within the builder.
 
 [source,java]
 ----
 builder.includePath("address");
 ----
 
-We could then go further and include details of every order in the customer's `orders` collection, and details of every
-product of every order:
+We could then go further and include details of every order in the customer's `orders` collection, and details of every product of every order:
 
 [source,java]
 ----
@@ -125,22 +138,16 @@ Element customerAsXml = xmlsnapshotService.builderFor(customer)
                         .getXmlElement();
 ----
 
-
 As you might imagine, the resultant XML document can get quite large very quickly with only a few "include"s.
 
-
 [NOTE]
 ====
 If an XSD schema is beng generated (using `snapshot.getXsdElement()` then note that for the XSD to be correct, the object being snapshotted must have non-null values for the paths that are `include()`'d. If this isn't done then the XSD will not be correct reflect for another snapshotted object that does have non-null values.
 ====
 
-
-
-
 == Automatic inclusions
 
-If the domain object being snapshotted implements the `SnapshottableWithInclusions` interace, then this moves the
-responsibility for determining what is included within the snapshot from the caller to the snapshottable object itself:
+If the domain object being snapshotted implements the `SnapshottableWithInclusions` interace, then this moves the responsibility for determining what is included within the snapshot from the caller to the snapshottable object itself:
 
 [source]
 ----
@@ -151,56 +158,25 @@ public interface SnapshottableWithInclusions extends Snapshottable {
 
 If necessary, both approaches can be combined.
 
-
 [TIP]
 ====
-As an alternative to using `include()`, you might consider building a view model domain object which can reference only the relevant information required for the snapshot. For example, if only the 5 most recent Orders for a Customer were required, a `CustomerAndRecentOrders` view model could hold a collection of just those 5 ``Order``s. Typically such view models would implement `SnapshottableWithInclusions`.
+As an alternative to using `include()`, you might consider building a view model domain object which can reference only the relevant information required for the snapshot.
+For example, if only the 5 most recent Orders for a Customer were required, a `CustomerAndRecentOrders` view model could hold a collection of just those 5 ``Order``s.
+Typically such view models would implement `SnapshottableWithInclusions`.
 
 One reason for doing this is to provide a stable API between the domain model and whatever it is that might be consuming the XML. With a view model you can refactor the domain entities but still preserve a view model such that the XML is the same.
 ====
 
-
-
-== Convenience API
-
-The `XmlSnapshotService` also provides some API for simply manipulating XML:
-
-[source]
-----
-public interface XmlSnapshotService {
-    ...
-    @Programmatic
-    public Document asDocument(String xmlStr);                          // <1>
-    @Programmatic
-    public <T> T getChildElementValue(                                  // <2>
-                    Element el, String tagname, Class<T> expectedCls);
-    @Programmatic
-    public Element getChildElement(                                     // <3>
-                    Element el, String tagname);
-    @Programmatic
-    public String getChildTextValue(Element el);                        // <4>
-}
-----
-<1> is a convenience method to convert xml string back into a W3C Document
-<2> is a convenience method to extract the value of an XML element, based on its type.
-<3> is a convenience method to walk XML document.
-<4> is a convenience method to obtain value of child text node.
-
-
-
-
-
 == Implementation
 
 The core framework provides an implementation of this service (`o.a.i.core.runtime.services.xmlsnapshot.XmlSnapshotServiceDefault`).
 
-
 == Related Services
 
 The xref:refguide:applib-svc:BookmarkService.adoc[`BookmarkService`] provides a mechanism for obtaining a string representations of a single domain object.
 
-The xref:refguide:applib-svc:JaxbService.adoc[`JaxbService`] is a simple wrapper around
-standard JAXB functionality for generating both XMLs and XSDs from JAXB-annotated classes.  Note that there is built-in support for JAXB classes (ie annotated with
+The xref:refguide:applib-svc:JaxbService.adoc[`JaxbService`] is a simple wrapper around standard JAXB functionality for generating both XMLs and XSDs from JAXB-annotated classes.
+Note that there is built-in support for JAXB classes (ie annotated with
 xref:refguide:applib-ant:XmlRootElement.adoc[`@XmlRootElement`]) to be used as view models.
 
 
diff --git a/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc b/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
index 21e7c90..54691db 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
@@ -64,5 +64,6 @@
 ** xref:refguide:applib-svc:UserRegistrationService.adoc[UserRegistrationService]
 ** xref:refguide:applib-svc:UserService.adoc[UserService]
 ** xref:refguide:applib-svc:WrapperFactory.adoc[WrapperFactory]
+** xref:refguide:applib-svc:XmlService.adoc[XmlService]
 ** xref:refguide:applib-svc:XmlSnapshotService.adoc[XmlSnapshotService]
 
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
index 578d883..fb5d805 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
@@ -66,26 +66,30 @@ public interface TransactionService {
     // tag::refguide[]
     void nextTransaction();                                 // <.>
 
-//    // end::refguide[]
-//
+    // end::refguide[]
+    /**
+     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
+     * boundary creates a new one.
+     *
+     * @param task
+     */
+    // tag::refguide[]
+    void executeWithinTransaction(Runnable task);           // <.>
+
+    // end::refguide[]
+    /**
+     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
+     * boundary creates a new one.
+     *
+     * @param task
+     */
+    // tag::refguide[]
+    <T> T executeWithinTransaction(Supplier<T> task);       // <.>
+
+    // end::refguide[]
+
+//   the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
 //   not sure there's any need for these additional methods?
-//   in any case, the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
-//
-//    /**
-//     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
-//     * boundary creates a new one.
-//     *
-//     * @param task
-//     */
-//    void executeWithinTransaction(Runnable task);           // <.>
-//
-//    /**
-//     * Runs given {@code task} within an existing transactional boundary, or in the absence of such a
-//     * boundary creates a new one.
-//     *
-//     * @param task
-//     */
-//    <T> T executeWithinTransaction(Supplier<T> task);       // <.>
 //
 //    /**
 //     * Runs given {@code task} within its own (new) transactional boundary.
@@ -100,6 +104,5 @@ public interface TransactionService {
 //    <T> T executeWithinNewTransaction(Supplier<T> task);    // <.>
 
     // tag::refguide[]
-
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java b/api/applib/src/main/java/org/apache/isis/applib/services/xml/XmlService.java
similarity index 51%
copy from api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
copy to api/applib/src/main/java/org/apache/isis/applib/services/xml/XmlService.java
index 07c399d..7081689 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xml/XmlService.java
@@ -16,43 +16,18 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.xmlsnapshot;
+package org.apache.isis.applib.services.xml;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService;
 
 /**
- * This service allows an XML document to be generated capturing the data of a root entity and specified related
- * entities.  This XML can be used for various purposes, such as mail merge/reporting, or adhoc auditing.
- *
- * <p>
- * The framework provides an implementation of this service (<tt>XmlSnapshotServiceDefault</tt>) which is automatically
- * registered and available for use; no further configuration is required.
- * </p>
+ * This service provides a number of utility services for interacting with XML {@link Document}s
  */
 // tag::refguide[]
-public interface XmlSnapshotService {
-
-    // end::refguide[]
-    // tag::refguide-1[]
-    interface Snapshot {
-        Document getXmlDocument();
-        Document getXsdDocument();
-
-        String getXmlDocumentAsString();
-        String getXsdDocumentAsString();
-    }
-    // end::refguide-1[]
-
-    // tag::refguide-2[]
-    interface Builder {
-        void includePath(final String path);
-        void includePathAndAnnotation(final String path, final String annotation);
-        XmlSnapshotService.Snapshot build();
-    }
-    // end::refguide-2[]
+public interface XmlService {
 
     // tag::refguide-3[]
     class Exception extends RuntimeException {
@@ -77,29 +52,25 @@ public interface XmlSnapshotService {
     }
     // end::refguide-3[]
 
-    // tag::refguide[]
-    XmlSnapshotService.Snapshot snapshotFor(                    // <.>
-                                    final Object domainObject);
-
-    XmlSnapshotService.Builder builderFor(                      // <.>
-                                    final Object domainObject);
 
     // end::refguide[]
     /**
-     * Convenience to convert xml string (eg as obtained by {@link Snapshot#getXmlDocumentAsString()} or
-     * {@link Snapshot#getXsdDocumentAsString()}) back into a {@link Document W3C Document}.
+     * Converts xml string into an {@link Document W3C Document}.
+     *
+     * @see #asString(Document)
      */
     // tag::refguide[]
     Document asDocument(String xmlStr);                         // <.>
 
     // end::refguide[]
+
     /**
-     * Convenience method to extract value of an XML element, based on its type.
+     * Serializes a {@link Document W3C Document} back into a string.
+     *
+     * @see #asDocument(String)
      */
     // tag::refguide[]
-    <T> T getChildElementValue(                                 // <.>
-            final Element el, final String tagname,
-            final Class<T> expectedCls);
+    public String asString(final Document doc);                 // <.>
 
     // end::refguide[]
     /**
@@ -107,7 +78,7 @@ public interface XmlSnapshotService {
      */
     // tag::refguide[]
     Element getChildElement(                                    // <.>
-            final Element el, final String tagname);
+                    final Element el, final String tagname);
 
     // end::refguide[]
     /**
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xml/package-info.java b/api/applib/src/main/java/org/apache/isis/applib/services/xml/package-info.java
new file mode 100644
index 0000000..3bc3341
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xml/package-info.java
@@ -0,0 +1,24 @@
+/*
+ *  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.
+ */
+
+/**
+ * The {@link org.apache.isis.applib.services.xml.XmlService} provides utilities for working with
+ * {@link org.w3c.dom.Document w3c DOM Document}s.
+ */
+package org.apache.isis.applib.services.xml;
\ No newline at end of file
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotService.java b/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotService.java
index 07c399d..a1d1388 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotService.java
@@ -21,8 +21,6 @@ package org.apache.isis.applib.services.xmlsnapshot;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.apache.isis.applib.annotation.Programmatic;
-
 /**
  * This service allows an XML document to be generated capturing the data of a root entity and specified related
  * entities.  This XML can be used for various purposes, such as mail merge/reporting, or adhoc auditing.
@@ -38,61 +36,30 @@ public interface XmlSnapshotService {
     // end::refguide[]
     // tag::refguide-1[]
     interface Snapshot {
-        Document getXmlDocument();
-        Document getXsdDocument();
-
-        String getXmlDocumentAsString();
-        String getXsdDocumentAsString();
-    }
-    // end::refguide-1[]
-
-    // tag::refguide-2[]
-    interface Builder {
-        void includePath(final String path);
-        void includePathAndAnnotation(final String path, final String annotation);
-        XmlSnapshotService.Snapshot build();
-    }
-    // end::refguide-2[]
-
-    // tag::refguide-3[]
-    class Exception extends RuntimeException {
-
-        private static final long serialVersionUID = 1L;
-
-        public Exception() {
-            super();
-        }
-
-        public Exception(String message, Throwable cause) {
-            super(message, cause);
-        }
-
-        public Exception(String message) {
-            super(message);
-        }
-
-        public Exception(Throwable cause) {
-            super(cause);
+        Document getXmlDocument();          // <.>
+        Document getXsdDocument();          // <.>
+        // end::refguide-1[]
+        // tag::refguide-2[]
+        interface Builder {
+            void includePath(final String path);                    // <.>
+            void includePathAndAnnotation(                          // <.>
+                final String path, final String annotation);
+            XmlSnapshotService.Snapshot build();                    // <.>
         }
+        // end::refguide-2[]
+        // tag::refguide-1[]
     }
-    // end::refguide-3[]
+    // end::refguide-1[]
 
     // tag::refguide[]
     XmlSnapshotService.Snapshot snapshotFor(                    // <.>
                                     final Object domainObject);
 
-    XmlSnapshotService.Builder builderFor(                      // <.>
+    XmlSnapshotService.Snapshot.Builder builderFor(             // <.>
                                     final Object domainObject);
 
     // end::refguide[]
-    /**
-     * Convenience to convert xml string (eg as obtained by {@link Snapshot#getXmlDocumentAsString()} or
-     * {@link Snapshot#getXsdDocumentAsString()}) back into a {@link Document W3C Document}.
-     */
-    // tag::refguide[]
-    Document asDocument(String xmlStr);                         // <.>
 
-    // end::refguide[]
     /**
      * Convenience method to extract value of an XML element, based on its type.
      */
@@ -101,20 +68,6 @@ public interface XmlSnapshotService {
             final Element el, final String tagname,
             final Class<T> expectedCls);
 
-    // end::refguide[]
-    /**
-     * Convenience method to walk XML document.
-     */
-    // tag::refguide[]
-    Element getChildElement(                                    // <.>
-            final Element el, final String tagname);
-
-    // end::refguide[]
-    /**
-     * Convenience method to obtain value of child text node.
-     */
-    // tag::refguide[]
-    String getChildTextValue(final Element el);                 // <.>
 
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstract.java
index 4374127..05d3b61 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstract.java
@@ -18,127 +18,21 @@
  */
 package org.apache.isis.applib.services.xmlsnapshot;
 
-import java.io.StringReader;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.Locale;
 
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.dom.DOMResult;
-import javax.xml.transform.stream.StreamSource;
+import javax.inject.Inject;
 
-import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
 
-import org.apache.isis.core.commons.internal.codec._DocumentFactories;
+import org.apache.isis.applib.services.xml.XmlService;
 
 public abstract class XmlSnapshotServiceAbstract implements XmlSnapshotService {
 
-    @Override
-    public Document asDocument(String xmlStr) {
-        try {
-            final StringReader reader = new StringReader(xmlStr);
-            final StreamSource streamSource = new StreamSource(reader);
-            final DOMResult result = new DOMResult();
-            
-            final Transformer transformer = _DocumentFactories.transformer();
-            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
-            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-            transformer.transform(streamSource, result);
-
-            final Node node = result.getNode();
-            return (Document) node;
-        } catch (TransformerException e) {
-            throw new XmlSnapshotService.Exception(e);
-        }
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> T getChildElementValue(final Element el, final String tagname, final Class<T> expectedCls) {
-        final Element chldEl = getChildElement(el, tagname);
-        final String dataType = chldEl.getAttribute("isis:datatype");
-        if(dataType == null) {
-            throw new IllegalArgumentException("unable to locate " + tagname + "/@datatype attribute");
-        }
-        if("isis:String".equals(dataType)) {
-            return (T)getChildTextValue(chldEl);
-        }
-        if("isis:LocalDate".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            final DateTimeFormatter parser = DateTimeFormatter
-                    .ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
-            return (T)parser.parse(str, LocalDate::from);
-        }
-        if("isis:Byte".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Byte.valueOf(str);
-        }
-        if("isis:Short".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Short.valueOf(str);
-        }
-        if("isis:Integer".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Integer.valueOf(str);
-        }
-        if("isis:Long".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Long.valueOf(str);
-        }
-        if("isis:Float".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Float.valueOf(str);
-        }
-        if("isis:Double".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Double.valueOf(str);
-        }
-        if("isis:BigDecimal".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) new BigDecimal(str);
-        }
-        if("isis:BigInteger".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) new BigInteger(str);
-        }
-        if("isis:Boolean".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) Boolean.valueOf(str);
-        }
-        throw new IllegalArgumentException(
-                "Datatype of '" + dataType + "' for element '" + tagname + "' not recognized");
-    }
-
-    @Override
-    public Element getChildElement(final Element el, final String tagname) {
-        NodeList elementsByTagName = el.getElementsByTagName(tagname);
-        final int length = elementsByTagName.getLength();
-        if(length != 1 || !(elementsByTagName.item(0) instanceof Element)) {
-            throw new IllegalArgumentException("unable to locate " + tagname + " element");
-        }
-        final Element item = (Element) elementsByTagName.item(0);
-        return item;
-    }
-
-    @Override
-    public String getChildTextValue(final Element el) {
-        final NodeList childNodes = el.getChildNodes();
-        if(childNodes.getLength() !=1 || !(childNodes.item(0) instanceof Text)) {
-            throw new IllegalArgumentException("unable to locate app:reference/text() node");
-        }
-        final Text referenceText = (Text) childNodes.item(0);
-        return referenceText.getData();
-    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
index cb69db7..7651f49 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/snapshot/XmlSnapshot.java
@@ -875,38 +875,5 @@ public class XmlSnapshot implements Snapshot {
         this.xmlElement = xmlElement;
     }
 
-    @Override
-    public String getXmlDocumentAsString() {
-        final Document doc = getXmlDocument();
-        return asString(doc);
-    }
-
-    @Override
-    public String getXsdDocumentAsString() {
-        final Document doc = getXsdDocument();
-        return asString(doc);
-    }
-
-    private static String asString(final Document doc) {
-        try {
-            final DOMSource domSource = new DOMSource(doc);
-            final StringWriter writer = new StringWriter();
-            final StreamResult result = new StreamResult(writer);
-            
-            final Transformer transformer = _DocumentFactories.transformer();
-            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
-            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-            transformer.transform(domSource, result);
-
-            return writer.toString();
-        } catch (TransformerConfigurationException e) {
-            throw new IsisException(e);
-        } catch (TransformerException e) {
-            throw new IsisException(e);
-        }
-    }
 
 }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
index 0ee6346..85bea18 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/IsisModuleCoreRuntimeServices.java
@@ -57,6 +57,7 @@ import org.apache.isis.core.runtimeservices.userprof.UserProfileServiceDefault;
 import org.apache.isis.core.runtimeservices.userreg.EmailNotificationServiceDefault;
 import org.apache.isis.core.runtimeservices.wrapper.WrapperFactoryDefault;
 import org.apache.isis.core.runtimeservices.xactn.TransactionServiceSpring;
+import org.apache.isis.core.runtimeservices.xml.XmlServiceDefault;
 import org.apache.isis.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefault;
 
 @Configuration
@@ -90,11 +91,12 @@ import org.apache.isis.core.runtimeservices.xmlsnapshot.XmlSnapshotServiceDefaul
         TransactionServiceSpring.class,
         UrlEncodingServiceWithCompression.class,
         UserProfileServiceDefault.class,
-        WrapperFactoryDefault.class,
-        XmlSnapshotServiceDefault.class,
         UserServiceDefault.class,
         UserServiceDefault.SudoServiceSpi.class,
-        
+        WrapperFactoryDefault.class,
+        XmlServiceDefault.class,
+        XmlSnapshotServiceDefault.class,
+
         // @Controller
         RoutingServiceDefault.class,
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
index 1828282..7c1b5c0 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/repository/RepositoryServiceDefault.java
@@ -30,11 +30,16 @@ import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Repository;
 
 import org.apache.isis.applib.NonRecoverableException;
 import org.apache.isis.applib.PersistFailedException;
 import org.apache.isis.applib.RepositoryException;
+import org.apache.isis.applib.annotation.IsisSessionScope;
+import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.query.Query;
 import org.apache.isis.applib.query.QueryFindAllInstances;
 import org.apache.isis.applib.services.factory.FactoryService;
@@ -50,9 +55,14 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.runtime.persistence.session.PersistenceSession;
 
 import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 @Repository
 @Named("isisRuntimeServices.RepositoryServiceDefault")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("Default")
+@Log4j2
 public class RepositoryServiceDefault implements RepositoryService {
 
     @Inject private FactoryService factoryService;
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/routing/RoutingServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/routing/RoutingServiceDefault.java
index c4712d9..9e1783f 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/routing/RoutingServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/routing/RoutingServiceDefault.java
@@ -21,13 +21,23 @@ package org.apache.isis.core.runtimeservices.routing;
 import javax.inject.Inject;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Controller;
 
+import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.routing.RoutingService;
 import org.apache.isis.applib.services.homepage.HomePageResolverService;
 
+import lombok.extern.log4j.Log4j2;
+
 @Controller
 @Named("isisRuntimeServices.RoutingServiceDefault")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("Default")
+@Log4j2
 public class RoutingServiceDefault implements RoutingService {
     
     private final HomePageResolverService homePageResolverService;
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
index 4c4b6b2..200c2c2 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/scratchpad/ScratchpadDefault.java
@@ -44,50 +44,40 @@ import lombok.extern.log4j.Log4j2;
  * {@link org.apache.isis.applib.annotation.DomainService}.  This means that it is automatically registered and
  * available for use; no further configuration is required.
  */
-// tag::refguide[]
 @Service
+@Named("isisRuntimeServices.Scratchpad")
 @Order(OrderPrecedence.EARLY)
 @Primary
-@Named("isisApplib.Scratchpad")
 @Qualifier("Default")
 @IsisSessionScope
 @Log4j2
 public class ScratchpadDefault implements Scratchpad {
 
-    // end::refguide[]
     /**
      * Provides a mechanism for each object being acted upon to pass
      * data to the next object.
      */
-    // tag::refguide[]
     private final Map<Object, Object> userData = _Maps.newHashMap();
 
-    // end::refguide[]
     /**
      * Obtain user-data, as set by a previous object being acted upon.
      */
-    // tag::refguide[]
     public Object get(Object key) {
         return userData.get(key);
     }
 
-    // end::refguide[]
     /**
      * Set user-data, for the use of a subsequent object being acted upon.
      */
-    // tag::refguide[]
     public void put(Object key, Object value) {
         userData.put(key, value);
     }
 
-    // end::refguide[]
     /**
      * Clear any user data.
      */
-    // tag::refguide[]
     public void clear() {
         userData.clear();
     }
 
 }
-// end::refguide[]
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
index 6d554fd..4d389d1 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xactn/TransactionServiceSpring.java
@@ -121,56 +121,52 @@ public class TransactionServiceSpring implements TransactionService {
         platformTransactionManager.getTransaction(transactionTemplate);
     }
 
-//    @Override
-//    public void executeWithinNewTransaction(Runnable task) {
-//        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
-//            // the code in this method executes in a transactional context
-//            @Override
-//            protected void doInTransactionWithoutResult(TransactionStatus status) {
-//                task.run();
-//            }
-//        });
-//    }
-//
-//    @Override
-//    public <T> T executeWithinNewTransaction(Supplier<T> task) {
-//
-//        return transactionTemplate.execute(new TransactionCallback<T>() {
-//            // the code in this method executes in a transactional context
-//            @Override
-//            public T doInTransaction(TransactionStatus status) {
-//                return task.get();
-//            }
-//        });
-//    }
-
-//    @Override
-//    public void executeWithinTransaction(Runnable task) {
-//
-//        val txState = currentTransactionState();
-//        if(txState != TransactionState.NONE) {
-//            task.run();
-//            flushTransaction();
-//            return;
-//        }
-//
-//        executeWithinNewTransaction(task);
-//
-//    }
-//
-//    @Override
-//    public <T> T executeWithinTransaction(Supplier<T> task) {
-//
-//        val txState = currentTransactionState();
-//        if(txState != TransactionState.NONE) {
-//            val result = task.get();
-//            flushTransaction();
-//            return result;
-//        }
-//
-//        return executeWithinNewTransaction(task);
-//
-//    }
+    private void executeWithinNewTransaction(Runnable task) {
+        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+            // the code in this method executes in a transactional context
+            @Override
+            protected void doInTransactionWithoutResult(TransactionStatus status) {
+                task.run();
+            }
+        });
+    }
+
+    private <T> T executeWithinNewTransaction(Supplier<T> task) {
+
+        return transactionTemplate.execute(new TransactionCallback<T>() {
+            // the code in this method executes in a transactional context
+            @Override
+            public T doInTransaction(TransactionStatus status) {
+                return task.get();
+            }
+        });
+    }
+
+    @Override
+    public void executeWithinTransaction(Runnable task) {
+
+        val txState = currentTransactionState();
+        if(txState != TransactionState.NONE) {
+            task.run();
+            flushTransaction();
+            return;
+        }
+
+        executeWithinNewTransaction(task);
+    }
+
+    @Override
+    public <T> T executeWithinTransaction(Supplier<T> task) {
+
+        val txState = currentTransactionState();
+        if(txState != TransactionState.NONE) {
+            val result = task.get();
+            flushTransaction();
+            return result;
+        }
+
+        return executeWithinNewTransaction(task);
+    }
 
     // -- HELPER
 
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotServiceAbstract.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xml/XmlServiceDefault.java
similarity index 55%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotServiceAbstract.java
rename to core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xml/XmlServiceDefault.java
index 4374127..7acd5e7 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xmlsnapshot/XmlSnapshotServiceAbstract.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xml/XmlServiceDefault.java
@@ -16,30 +16,52 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.xmlsnapshot;
+package org.apache.isis.core.runtimeservices.xml;
 
 import java.io.StringReader;
+import java.io.StringWriter;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.Locale;
 
+import javax.inject.Named;
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 import javax.xml.transform.stream.StreamSource;
 
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
 
+import org.apache.isis.applib.annotation.IsisSessionScope;
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.xml.XmlService;
+import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService;
+import org.apache.isis.core.commons.exceptions.IsisException;
 import org.apache.isis.core.commons.internal.codec._DocumentFactories;
 
-public abstract class XmlSnapshotServiceAbstract implements XmlSnapshotService {
+import lombok.extern.log4j.Log4j2;
+
+@Service
+@Named("isisRuntimeServices.XmlService")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("Default")
+@Log4j2
+public class XmlServiceDefault implements XmlService {
 
     @Override
     public Document asDocument(String xmlStr) {
@@ -47,7 +69,7 @@ public abstract class XmlSnapshotServiceAbstract implements XmlSnapshotService {
             final StringReader reader = new StringReader(xmlStr);
             final StreamSource streamSource = new StreamSource(reader);
             final DOMResult result = new DOMResult();
-            
+
             final Transformer transformer = _DocumentFactories.transformer();
             transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
             transformer.setOutputProperty(OutputKeys.METHOD, "xml");
@@ -59,65 +81,31 @@ public abstract class XmlSnapshotServiceAbstract implements XmlSnapshotService {
             final Node node = result.getNode();
             return (Document) node;
         } catch (TransformerException e) {
-            throw new XmlSnapshotService.Exception(e);
+            throw new Exception(e);
         }
     }
 
     @Override
-    @SuppressWarnings("unchecked")
-    public <T> T getChildElementValue(final Element el, final String tagname, final Class<T> expectedCls) {
-        final Element chldEl = getChildElement(el, tagname);
-        final String dataType = chldEl.getAttribute("isis:datatype");
-        if(dataType == null) {
-            throw new IllegalArgumentException("unable to locate " + tagname + "/@datatype attribute");
-        }
-        if("isis:String".equals(dataType)) {
-            return (T)getChildTextValue(chldEl);
-        }
-        if("isis:LocalDate".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            final DateTimeFormatter parser = DateTimeFormatter
-                    .ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
-            return (T)parser.parse(str, LocalDate::from);
-        }
-        if("isis:Byte".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Byte.valueOf(str);
-        }
-        if("isis:Short".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Short.valueOf(str);
-        }
-        if("isis:Integer".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Integer.valueOf(str);
-        }
-        if("isis:Long".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Long.valueOf(str);
-        }
-        if("isis:Float".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Float.valueOf(str);
-        }
-        if("isis:Double".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T)Double.valueOf(str);
-        }
-        if("isis:BigDecimal".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) new BigDecimal(str);
-        }
-        if("isis:BigInteger".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) new BigInteger(str);
-        }
-        if("isis:Boolean".equals(dataType)) {
-            final String str = getChildTextValue(chldEl);
-            return (T) Boolean.valueOf(str);
+    public String asString(final Document doc) {
+        try {
+            final DOMSource domSource = new DOMSource(doc);
+            final StringWriter writer = new StringWriter();
+            final StreamResult result = new StreamResult(writer);
+
+            final Transformer transformer = _DocumentFactories.transformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+            transformer.transform(domSource, result);
+
+            return writer.toString();
+        } catch (TransformerConfigurationException e) {
+            throw new IsisException(e);
+        } catch (TransformerException e) {
+            throw new IsisException(e);
         }
-        throw new IllegalArgumentException(
-                "Datatype of '" + dataType + "' for element '" + tagname + "' not recognized");
     }
 
     @Override
@@ -135,7 +123,7 @@ public abstract class XmlSnapshotServiceAbstract implements XmlSnapshotService {
     public String getChildTextValue(final Element el) {
         final NodeList childNodes = el.getChildNodes();
         if(childNodes.getLength() !=1 || !(childNodes.item(0) instanceof Text)) {
-            throw new IllegalArgumentException("unable to locate app:reference/text() node");
+            throw new IllegalArgumentException("unable to locate child Text node");
         }
         final Text referenceText = (Text) childNodes.item(0);
         return referenceText.getData();
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
index 21cf984..17f26c5 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault.java
@@ -18,6 +18,12 @@
  */
 package org.apache.isis.core.runtimeservices.xmlsnapshot;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -25,8 +31,10 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
+import org.w3c.dom.Element;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.xml.XmlService;
 import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService;
 import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotServiceAbstract;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -47,11 +55,20 @@ import org.apache.isis.core.metamodel.util.snapshot.XmlSnapshot;
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("Default")
-public class XmlSnapshotServiceDefault extends XmlSnapshotServiceAbstract {
+public class XmlSnapshotServiceDefault implements XmlSnapshotService {
     
-    @Inject private SpecificationLoader specificationLoader;
+    private final XmlService xmlService;
+    private final SpecificationLoader specificationLoader;
+
+    @Inject
+    public XmlSnapshotServiceDefault(
+            final XmlService xmlService,
+            final SpecificationLoader specificationLoader) {
+        this.xmlService = xmlService;
+        this.specificationLoader = specificationLoader;
+    }
 
-    static class XmlSnapshotServiceDefaultBuilder implements XmlSnapshotService.Builder{
+    static class XmlSnapshotServiceDefaultBuilder implements XmlSnapshotService.Snapshot.Builder{
 
         private final XmlSnapshotBuilder builder;
         public XmlSnapshotServiceDefaultBuilder(SpecificationLoader specificationLoader, Object domainObject) {
@@ -86,13 +103,71 @@ public class XmlSnapshotServiceDefault extends XmlSnapshotServiceAbstract {
 
     /**
      * Creates a builder that allows a custom snapshot - traversing additional associated
-     * properties or collections (using {@link Builder#includePath(String)} and
-     * {@link Builder#includePathAndAnnotation(String, String)}) - to be created.
+     * properties or collections (using {@link Snapshot.Builder#includePath(String)} and
+     * {@link Snapshot.Builder#includePathAndAnnotation(String, String)}) - to be created.
      */
     @Override
-    public Builder builderFor(final Object domainObject) {
+    public Snapshot.Builder builderFor(final Object domainObject) {
         return new XmlSnapshotServiceDefaultBuilder(specificationLoader, domainObject);
     }
 
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getChildElementValue(final Element el, final String tagname, final Class<T> expectedCls) {
+        final Element chldEl = xmlService.getChildElement(el, tagname);
+        final String dataType = chldEl.getAttribute("isis:datatype");
+        if(dataType == null) {
+            throw new IllegalArgumentException(String.format("unable to locate %s/@datatype attribute", tagname));
+        }
+        if("isis:String".equals(dataType)) {
+            return (T)xmlService.getChildTextValue(chldEl);
+        }
+        if("isis:LocalDate".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            final DateTimeFormatter parser = DateTimeFormatter
+                    .ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
+            return (T)parser.parse(str, LocalDate::from);
+        }
+        if("isis:Byte".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Byte.valueOf(str);
+        }
+        if("isis:Short".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Short.valueOf(str);
+        }
+        if("isis:Integer".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Integer.valueOf(str);
+        }
+        if("isis:Long".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Long.valueOf(str);
+        }
+        if("isis:Float".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Float.valueOf(str);
+        }
+        if("isis:Double".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T)Double.valueOf(str);
+        }
+        if("isis:BigDecimal".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T) new BigDecimal(str);
+        }
+        if("isis:BigInteger".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T) new BigInteger(str);
+        }
+        if("isis:Boolean".equals(dataType)) {
+            final String str = xmlService.getChildTextValue(chldEl);
+            return (T) Boolean.valueOf(str);
+        }
+        throw new IllegalArgumentException(
+                String.format("Datatype of '%s' for element '%s' not recognized", dataType, tagname));
+    }
+
+
 }
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstractTest.xml b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceAbstractTest.xml
similarity index 100%
rename from api/applib/src/test/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstractTest.xml
rename to core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceAbstractTest.xml
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstractTest.java b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault_Test.java
similarity index 85%
rename from api/applib/src/test/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstractTest.java
rename to core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault_Test.java
index 2eab325..48f46c7 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/services/xmlsnapshot/XmlSnapshotServiceAbstractTest.java
+++ b/core/runtimeservices/src/test/java/org/apache/isis/core/runtimeservices/xmlsnapshot/XmlSnapshotServiceDefault_Test.java
@@ -14,7 +14,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.isis.applib.services.xmlsnapshot;
+package org.apache.isis.core.runtimeservices.xmlsnapshot;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -30,21 +30,25 @@ import org.w3c.dom.Element;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
+import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotService;
+import org.apache.isis.applib.services.xmlsnapshot.XmlSnapshotServiceAbstract;
 import org.apache.isis.core.commons.internal.resources._Resources;
+import org.apache.isis.core.runtimeservices.xml.XmlServiceDefault;
 
-public class XmlSnapshotServiceAbstractTest {
+public class XmlSnapshotServiceDefault_Test {
 
-    private XmlSnapshotServiceAbstract xmlSnapshotService;
+    private XmlSnapshotServiceDefault xmlSnapshotService;
     private String xmlStr;
+    private XmlServiceDefault xmlService;
 
     @Before
     public void setUp() throws Exception {
         xmlStr = _Resources.loadAsString(
-                XmlSnapshotServiceAbstractTest.class, 
+                XmlSnapshotServiceDefault_Test.class,
                 "XmlSnapshotServiceAbstractTest.xml", 
                 Charset.forName("UTF-8"));
-        xmlSnapshotService = new XmlSnapshotServiceForUnitTesting();
-
+        xmlService = new XmlServiceDefault();
+        xmlSnapshotService = new XmlSnapshotServiceDefault(xmlService, null);
     }
 
 
@@ -59,7 +63,7 @@ public class XmlSnapshotServiceAbstractTest {
 
                 Locale.setDefault(eachLocal);
 
-                Document xmlDoc = xmlSnapshotService.asDocument(xmlStr);
+                Document xmlDoc = xmlService.asDocument(xmlStr);
                 Element rootEl = xmlDoc.getDocumentElement();
 
                 assertThat(
@@ -99,17 +103,4 @@ public class XmlSnapshotServiceAbstractTest {
     }
 
 
-    static class XmlSnapshotServiceForUnitTesting extends XmlSnapshotServiceAbstract {
-
-        @Override
-        public Snapshot snapshotFor(Object domainObject) {
-            throw new RuntimeException();
-        }
-
-        @Override
-        public Builder builderFor(Object domainObject) {
-            throw new RuntimeException();
-        }
-    }
-
 }