You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/06/17 10:34:52 UTC

[isis] 01/01: ISIS-2112: backporting smoketests from 'v2_SpringBoot'

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

ahuber pushed a commit to branch v2_winter
in repository https://gitbox.apache.org/repos/asf/isis.git

commit d7e91bc3f3fcc2e9fd443233d7e6324b7eb26121
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Jun 17 12:33:32 2019 +0200

    ISIS-2112: backporting smoketests from 'v2_SpringBoot'
    
    - to allow side-by-side debugging of these 2 branches
---
 core/commons/pom.xml                               | 239 ++++++++-------
 .../commons/internal/collections/_Collections.java |  17 +-
 .../isis/commons/internal/collections/_Sets.java   |  26 +-
 .../isis/commons/internal/resources/_Json.java     | 329 +++++++++++++++++++++
 .../commons/internal/resources/_Resources.java     |  12 +
 .../apache/isis/core/commons/collections/Bin.java  |  35 ++-
 .../persistence/PersistenceSessionFactory5.java    |   6 +-
 .../core/runtime/system/context/IsisContext.java   |   5 +
 .../src/main/webapp/WEB-INF/logging.properties     |   4 +-
 example/misc/smoketest/log4j2-test.xml             |  57 ++++
 .../misc/smoketest/logging-integtest.properties    | 120 ++++++++
 example/misc/smoketest/pom.xml                     | 239 +++++++++++++++
 .../java/org/apache/isis/testdomain/jdo/Book.java  |  82 +++++
 .../org/apache/isis/testdomain/jdo/Inventory.java  |  59 ++++
 .../isis/testdomain/jdo/JdoTestDomainPersona.java  | 100 +++++++
 .../apache/isis/testdomain/jdo/JdoTestModule.java  |   7 +
 .../org/apache/isis/testdomain/jdo/Product.java    |  63 ++++
 .../testdomain/jdo/isis-non-changing.properties    |  68 +++++
 .../isis/testdomain/tests/AuditerServiceTest.java  | 130 ++++++++
 .../apache/isis/testdomain/tests/H2Console.java    |  11 +
 .../testdomain/tests/JdoBootstrappingTest.java     | 134 +++++++++
 .../isis/testdomain/tests/JdoTransactionTest.java  | 124 ++++++++
 .../testdomain/tests/PublisherServiceTest.java     | 129 ++++++++
 .../tests/SpringEventPublishingTest.java           |  87 ++++++
 .../tests/SpringServiceProvisioningTest.java       |  63 ++++
 .../isis/testdomain/tests/builtin-IsisBoot.json    |  30 ++
 .../testdomain/tests/builtin-requestscoped.json    |  10 +
 .../isis/testdomain/tests/builtin-singleton.json   |  75 +++++
 example/misc/smoketest/translations.pot            |   0
 29 files changed, 2135 insertions(+), 126 deletions(-)

diff --git a/core/commons/pom.xml b/core/commons/pom.xml
index e48d652..54db1fd 100644
--- a/core/commons/pom.xml
+++ b/core/commons/pom.xml
@@ -1,129 +1,142 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
 
-    <parent>
-        <groupId>org.apache.isis.core</groupId>
-        <artifactId>isis</artifactId>
-        <version>${revision}</version>
-    </parent>
+	<parent>
+		<groupId>org.apache.isis.core</groupId>
+		<artifactId>isis</artifactId>
+		<version>${revision}</version>
+	</parent>
 
-    <artifactId>isis-core-commons</artifactId>
+	<artifactId>isis-core-commons</artifactId>
 
-    <name>Apache Isis Commons</name>
-    <description>
+	<name>Apache Isis Commons</name>
+	<description>
         Isis library, defining utilities required by both Applib and Unittestsupport.
     </description>
 
-    <properties>
-        <jar-plugin.automaticModuleName>org.apache.isis.commons</jar-plugin.automaticModuleName>
-        <git-plugin.propertiesDir>org/apache/isis/commons</git-plugin.propertiesDir>
-        
-        <lombok.version>[1.18.,)</lombok.version>
-    </properties>
+	<properties>
+		<jar-plugin.automaticModuleName>org.apache.isis.commons</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/isis/commons</git-plugin.propertiesDir>
 
-    <build>
-        <resources>
-            <resource>
-                <filtering>false</filtering>
-                <directory>src/main/resources</directory>
-            </resource>
-            <resource>
-                <filtering>false</filtering>
-                <directory>src/main/java</directory>
-                <includes>
-                    <include>**</include>
-                </includes>
-                <excludes>
-                    <exclude>**/*.java</exclude>
-                </excludes>
-            </resource>
-        </resources>
-    </build>
+		<lombok.version>[1.18.,)</lombok.version>
+	</properties>
 
-    <dependencies>
-    
-    	<!-- JEE API -->
-        <dependency>
-		    <groupId>javax</groupId>
-		    <artifactId>javaee-api</artifactId>
+	<build>
+		<resources>
+			<resource>
+				<filtering>false</filtering>
+				<directory>src/main/resources</directory>
+			</resource>
+			<resource>
+				<filtering>false</filtering>
+				<directory>src/main/java</directory>
+				<includes>
+					<include>**</include>
+				</includes>
+				<excludes>
+					<exclude>**/*.java</exclude>
+				</excludes>
+			</resource>
+		</resources>
+	</build>
+
+	<dependencies>
+
+		<!-- JEE API -->
+		<dependency>
+			<groupId>javax</groupId>
+			<artifactId>javaee-api</artifactId>
 		</dependency>
 
 		<!-- provides @Nullable -->
-        <dependency>
-        	<groupId>com.google.code.findbugs</groupId>
-        	<artifactId>annotations</artifactId>
-        </dependency>
-        
-        <dependency>
-        	<groupId>org.projectlombok</groupId>
-        	<artifactId>lombok</artifactId>
-        	<version>${lombok.version}</version>
-        </dependency>
+		<dependency>
+			<groupId>com.google.code.findbugs</groupId>
+			<artifactId>annotations</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<version>${lombok.version}</version>
+		</dependency>
+		
+		<!-- JACKSON -->
+
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.module</groupId>
+			<artifactId>jackson-module-jaxb-annotations</artifactId>
+		</dependency>
 
-    </dependencies>
+	</dependencies>
 
-    <profiles>
-        <profile>
-            <id>flatten</id>
-            <activation>
-                <property>
-                    <name>!skip.flatten</name>
-                </property>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>flatten-maven-plugin</artifactId>
-                        <version>1.0.0</version>
-                        <executions>
-                            <execution>
-                                <id>flatten</id>
-                                <phase>process-resources</phase>
-                                <goals>
-                                    <goal>flatten</goal>
-                                </goals>
-                                <configuration>
-                                    <flattenMode>defaults</flattenMode>
-                                    <updatePomFile>true</updatePomFile>
-                                    <pomElements>
-                                        <name>resolve</name>
-                                        <description>resolve</description>
-                                        <dependencies>resolve</dependencies>
-                                    </pomElements>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>flatten.clean</id>
-                                <phase>clean</phase>
-                                <goals>
-                                    <goal>clean</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
+	<profiles>
+		<profile>
+			<id>flatten</id>
+			<activation>
+				<property>
+					<name>!skip.flatten</name>
+				</property>
+			</activation>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>flatten-maven-plugin</artifactId>
+						<version>1.0.0</version>
+						<executions>
+							<execution>
+								<id>flatten</id>
+								<phase>process-resources</phase>
+								<goals>
+									<goal>flatten</goal>
+								</goals>
+								<configuration>
+									<flattenMode>defaults</flattenMode>
+									<updatePomFile>true</updatePomFile>
+									<pomElements>
+										<name>resolve</name>
+										<description>resolve</description>
+										<dependencies>resolve</dependencies>
+									</pomElements>
+								</configuration>
+							</execution>
+							<execution>
+								<id>flatten.clean</id>
+								<phase>clean</phase>
+								<goals>
+									<goal>clean</goal>
+								</goals>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
 
 </project>
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Collections.java b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Collections.java
index ea42a3e..8e9917b 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Collections.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Collections.java
@@ -19,8 +19,6 @@
 
 package org.apache.isis.commons.internal.collections;
 
-import static org.apache.isis.commons.internal.base._With.requires;
-
 import java.lang.reflect.Field;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -43,6 +41,8 @@ import javax.annotation.Nullable;
 
 import org.apache.isis.commons.internal.base._NullSafe;
 
+import static org.apache.isis.commons.internal.base._With.requires;
+
 /**
  * <h1>- internal use only -</h1>
  * <p>
@@ -308,6 +308,17 @@ public final class _Collections {
         return inferElementTypeIfAny(field.getType(), field.getGenericType());
     }
 
-    // --
+    // -- TO STRING
+    
+    public static String toStringJoining(@Nullable Collection<?> collecion, String delimiter) {
+        requires(delimiter, "delimiter");
+        return _NullSafe.stream(collecion)
+                .map(x->""+x)
+                .collect(Collectors.joining(delimiter));
+    }
+    
+    public static String toStringJoiningNewLine(@Nullable Collection<?> collecion) {
+        return toStringJoining(collecion, "\n");
+    }
 
 }
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Sets.java b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Sets.java
index 4266d58..bb1314c 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Sets.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/collections/_Sets.java
@@ -19,14 +19,13 @@
 
 package org.apache.isis.commons.internal.collections;
 
-import static org.apache.isis.commons.internal.base._With.requires;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
+import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentHashMap.KeySetView;
@@ -38,6 +37,8 @@ import javax.annotation.Nullable;
 
 import org.apache.isis.commons.internal.base._NullSafe;
 
+import static org.apache.isis.commons.internal.base._With.requires;
+
 /**
  * <h1>- internal use only -</h1>
  * <p>
@@ -224,8 +225,25 @@ public final class _Sets {
                 .filter(b::contains)
                 .collect(Collectors.toSet()) );
     }
-
-    // --
+    
+    /**
+     * Returns the intersection (set theory) of two given sets, retaining order.
+     * @param a
+     * @param b
+     * @return non null, unmodifiable
+     */
+    public static <T> SortedSet<T> intersectSorted(@Nullable SortedSet<T> a, @Nullable SortedSet<T> b) {
+        if(a==null && b==null) {
+            return Collections.emptySortedSet();
+        }
+        if(a==null || b==null) {
+            return Collections.emptySortedSet();
+        }
+        return Collections.unmodifiableSortedSet(
+                a.stream()
+                .filter(b::contains)
+                .collect(Collectors.toCollection(TreeSet::new)));
+    }
 
 
 }
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Json.java b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Json.java
new file mode 100644
index 0000000..6193da9
--- /dev/null
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Json.java
@@ -0,0 +1,329 @@
+/*
+ *  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 org.apache.isis.commons.internal.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.isis.commons.internal.base._Either;
+
+import lombok.val;
+
+/**
+ * <h1>- internal use only -</h1>
+ * <p>
+ * Utilities for the JSON format.
+ * </p>
+ * <p>
+ * <b>WARNING</b>: Do <b>NOT</b> use any of the classes provided by this package! <br/>
+ * These may be changed or removed without notice!
+ * </p>
+ * @since 2.0.0
+ */
+public class _Json {
+    
+    // -- STREAM CONTENT
+    
+    /**
+     * Deserialize JSON content from given JSON content InputStream into an instance of 
+     * given {@code clazz} type.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> T readJson(final Class<T> clazz, InputStream content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        return (T) new ObjectMapper().readValue(content, clazz);
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content InputStream into an instance of 
+     * given {@code clazz} type, or any exception that occurred during parsing.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     */
+    public static <T> _Either<T, Exception> tryReadJson(final Class<T> clazz, InputStream content) {
+        try {
+            return _Either.left(readJson(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    /**
+     * Deserialize JSON content from given JSON content InputStream into an instance of List 
+     * with given {@code elementType}.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> List<T> readJsonList(final Class<T> elementType, InputStream content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        val mapper = new ObjectMapper();
+        val listFactory = mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+        return mapper.readValue(content, listFactory);    
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content InputStream into an instance of List
+     * with given {@code elementType}, or any exception that occurred during parsing.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     */
+    public static <T> _Either<List<T>, Exception> tryReadJsonList(final Class<T> clazz, InputStream content) {
+        try {
+            return _Either.left(readJsonList(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+
+    
+    // -- STRING CONTENT
+    
+    /**
+     * Deserialize JSON content from given JSON content String into an instance of 
+     * given {@code clazz} type.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> T readJson(final Class<T> clazz, String content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        return (T) new ObjectMapper().readValue(content, clazz);
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content String into an instance of 
+     * given {@code clazz} type, or any exception that occurred during parsing.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     */
+    public static <T> _Either<T, Exception> tryReadJson(final Class<T> clazz, String content) {
+        try {
+            return _Either.left(readJson(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    /**
+     * Deserialize JSON content from given JSON content String into an instance of List 
+     * with given {@code elementType}.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> List<T> readJsonList(final Class<T> elementType, String content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        val mapper = new ObjectMapper();
+        val listFactory = mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+        return mapper.readValue(content, listFactory);    
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content String into an instance of List
+     * with given {@code elementType}, or any exception that occurred during parsing.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     */
+    public static <T> _Either<List<T>, Exception> tryReadJsonList(final Class<T> clazz, String content) {
+        try {
+            return _Either.left(readJsonList(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    
+    // -- FILE CONTENT
+    
+    /**
+     * Deserialize JSON content from given JSON content File into an instance of 
+     * given {@code clazz} type.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> T readJson(final Class<T> clazz, File content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        return (T) new ObjectMapper().readValue(content, clazz);
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content File into an instance of 
+     * given {@code clazz} type, or any exception that occurred during parsing.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     */
+    public static <T> _Either<T, Exception> tryReadJson(final Class<T> clazz, File content) {
+        try {
+            return _Either.left(readJson(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    /**
+     * Deserialize JSON content from given JSON content File into an instance of List 
+     * with given {@code elementType}.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> List<T> readJsonList(final Class<T> elementType, File content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        val mapper = new ObjectMapper();
+        val listFactory = mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+        return mapper.readValue(content, listFactory);    
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content File into an instance of List
+     * with given {@code elementType}, or any exception that occurred during parsing.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     */
+    public static <T> _Either<List<T>, Exception> tryReadJsonList(final Class<T> clazz, File content) {
+        try {
+            return _Either.left(readJsonList(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    // -- BYTE CONTENT
+    
+    /**
+     * Deserialize JSON content from given JSON content byte[] into an instance of 
+     * given {@code clazz} type.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> T readJson(final Class<T> clazz, byte[] content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        return (T) new ObjectMapper().readValue(content, clazz);
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content byte[] into an instance of 
+     * given {@code clazz} type, or any exception that occurred during parsing.
+     * @param <T>
+     * @param clazz
+     * @param content
+     * @return
+     */
+    public static <T> _Either<T, Exception> tryReadJson(final Class<T> clazz, byte[] content) {
+        try {
+            return _Either.left(readJson(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+    /**
+     * Deserialize JSON content from given JSON content byte[] into an instance of List 
+     * with given {@code elementType}.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     * @throws JsonParseException
+     * @throws JsonMappingException
+     * @throws IOException
+     */
+    public static <T> List<T> readJsonList(final Class<T> elementType, byte[] content) 
+            throws JsonParseException, JsonMappingException, IOException {
+        
+        val mapper = new ObjectMapper();
+        val listFactory = mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+        return mapper.readValue(content, listFactory);    
+    }
+    
+    /**
+     * Either deserialize JSON content from given JSON content byte[] into an instance of List
+     * with given {@code elementType}, or any exception that occurred during parsing.
+     * @param <T>
+     * @param elementType
+     * @param content
+     * @return
+     */
+    public static <T> _Either<List<T>, Exception> tryReadJsonList(final Class<T> clazz, byte[] content) {
+        try {
+            return _Either.left(readJsonList(clazz, content));
+        } catch (Exception e) {
+            return _Either.right(e);
+        }
+    }
+    
+}
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resources.java b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resources.java
index b01d3a9..3a4f2b7 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resources.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resources.java
@@ -131,6 +131,16 @@ public final class _Resources {
             return "/" + contextPath + path;
         }
     }
+    
+    public static String prependContextPathIfRequired(String url) {
+        if(url==null) {
+            return null; 
+        }
+        if(isLocalResource(url)) {
+            return prependContextPathIfPresent(url);
+        }
+        return url;
+    }
 
     // -- RESTFUL PATH RESOURCE
 
@@ -217,6 +227,8 @@ public final class _Resources {
         }
         return name;
     }
+
+
     
 
 }
diff --git a/core/commons/src/main/java/org/apache/isis/core/commons/collections/Bin.java b/core/commons/src/main/java/org/apache/isis/core/commons/collections/Bin.java
index 2d565a0..5c6e8af 100644
--- a/core/commons/src/main/java/org/apache/isis/core/commons/collections/Bin.java
+++ b/core/commons/src/main/java/org/apache/isis/core/commons/collections/Bin.java
@@ -1,7 +1,5 @@
 package org.apache.isis.core.commons.collections;
 
-import static org.apache.isis.commons.internal.base._With.requires;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -17,6 +15,8 @@ import javax.enterprise.inject.Instance;
 
 import org.apache.isis.commons.internal.base._NullSafe;
 
+import static org.apache.isis.commons.internal.base._With.requires;
+
 import lombok.val;
 
 /**
@@ -55,7 +55,7 @@ public interface Bin<T> extends Iterable<T> {
 		return Bin_Singleton.of(element);
 	}
 	
-	public static <T> Bin<T> ofCollection(@Nullable Collection<T> collection){
+	public static <T> Bin<T> ofCollection(@Nullable Collection<T> collection) {
 		
 		if(_NullSafe.size(collection)==0) {
 			return empty();
@@ -79,9 +79,36 @@ public interface Bin<T> extends Iterable<T> {
 			return ofSingleton(((List<T>)nonNullElements).get(0));
 		}
 		
+		//nonNullElements.sort(AnnotationAwareOrderComparator.INSTANCE);
+		
 		return Bin_Multiple.of(nonNullElements);
 	}
 	
+	public static <T> Bin<T> ofStream(@Nullable Stream<T> stream) {
+        
+        if(stream==null) {
+            return empty();
+        }
+        
+        val nonNullElements = stream
+                .filter(_NullSafe::isPresent)
+                .collect(Collectors.toCollection(()->new ArrayList<>()));        
+        
+        val size = nonNullElements.size();
+        
+        if(size==0) {
+            return empty();
+        }
+        
+        if(size==1) {
+            return ofSingleton(((List<T>)nonNullElements).get(0));
+        }
+        
+        //nonNullElements.sort(AnnotationAwareOrderComparator.INSTANCE);
+        
+        return Bin_Multiple.of(nonNullElements);
+    }
+	
 	public static <T> Bin<T> ofInstance(@Nullable Instance<T> instance) {
 		if(instance==null || instance.isUnsatisfied()) {
 			return empty();
@@ -92,6 +119,8 @@ public interface Bin<T> extends Iterable<T> {
 		val nonNullElements = instance.stream()
 				.collect(Collectors.toCollection(()->new ArrayList<>()));
 		
+		//nonNullElements.sort(AnnotationAwareOrderComparator.INSTANCE);
+		
 		return Bin_Multiple.of(nonNullElements);
 		
 	}
diff --git a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory5.java b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory5.java
index 6c4d931..469f796 100644
--- a/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory5.java
+++ b/core/plugins/jdo-datanucleus-5/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory5.java
@@ -148,12 +148,16 @@ implements PersistenceSessionFactory, ApplicationScopedComponent, FixturesInstal
             return;
         } else {
             // use JDBC connection properties; put if not present
-            LOG.info("did *not* find config properties to use JNDI datasource; will use JDBC");
 
             putIfNotPresent(props, "javax.jdo.option.ConnectionDriverName", "org.hsqldb.jdbcDriver");
             putIfNotPresent(props, "javax.jdo.option.ConnectionURL", "jdbc:hsqldb:mem:test");
             putIfNotPresent(props, "javax.jdo.option.ConnectionUserName", "sa");
             putIfNotPresent(props, "javax.jdo.option.ConnectionPassword", "");
+            
+            if(LOG.isInfoEnabled()) {
+                LOG.info("using JDBC connection '{}'", 
+                        props.get("javax.jdo.option.ConnectionURL"));
+            }
         }
     }
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
index b4d2818..dc19c71 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
@@ -191,6 +191,11 @@ public interface IsisContext {
     public static ServicesInjector getServicesInjector() {
         return getSessionFactory().getServicesInjector();
     }
+    
+    public static ServicesInjector getServiceRegistry() {
+        return getSessionFactory().getServicesInjector();
+    }
+    
 
     public static StringBuilder dumpConfig() {
         
diff --git a/example/application/helloworld/src/main/webapp/WEB-INF/logging.properties b/example/application/helloworld/src/main/webapp/WEB-INF/logging.properties
index b2d87e0..7b5c0b7 100644
--- a/example/application/helloworld/src/main/webapp/WEB-INF/logging.properties
+++ b/example/application/helloworld/src/main/webapp/WEB-INF/logging.properties
@@ -125,8 +125,8 @@ log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console, sql-ddl
 #log4j.logger.DataNucleus.Datastore.Native=WARN, Console, sql-dml
 #log4j.logger.DataNucleus.Datastore.Schema=WARN, Console, sql-ddl
 # the remainder can probably be left to WARN
-log4j.logger.DataNucleus.Persistence=WARN, Console
-log4j.logger.DataNucleus.Transaction=WARN, Console
+log4j.logger.DataNucleus.Persistence=DEBUG, Console
+log4j.logger.DataNucleus.Transaction=DEBUG, Console
 log4j.logger.DataNucleus.Connection=WARN, Console
 log4j.logger.DataNucleus.Query=WARN, Console
 log4j.logger.DataNucleus.Cache=WARN, Console
diff --git a/example/misc/smoketest/log4j2-test.xml b/example/misc/smoketest/log4j2-test.xml
new file mode 100644
index 0000000..6095740
--- /dev/null
+++ b/example/misc/smoketest/log4j2-test.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Configuration status="WARN">
+	<Properties>
+		<Property name="PID">????</Property>
+		<Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
+		<Property name="LOG_LEVEL_PATTERN">%5p</Property>
+		<Property name="LOG_DATEFORMAT_PATTERN">yyyy-MM-dd HH:mm:ss.SSS</Property>
+		<Property name="CONSOLE_LOG_PATTERN">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+		<Property name="FILE_LOG_PATTERN">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
+	</Properties>
+	<Appenders>
+		<Console name="Console" target="SYSTEM_OUT" follow="true">
+			<PatternLayout pattern="${sys:CONSOLE_LOG_PATTERN}" />
+		</Console>
+	</Appenders>
+	<Loggers>
+		<Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
+		<Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
+		<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
+		<logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
+		<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
+		<Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
+		<Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
+		<logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
+		
+		<logger name="DataNucleus.Persistence" level="debug"/>
+		<logger name="DataNucleus.Transaction" level="debug"/>
+		<logger name="DataNucleus.Datastore.Schema" level="debug"/>
+		<logger name="DataNucleus.Datastore.Native" level="debug"/>
+		
+		<Root level="info">
+			<AppenderRef ref="Console" />
+		</Root>
+	</Loggers>
+</Configuration>
+
+<!-- # DataNucleus Logging Categories -->
+<!-- DataNucleus.Persistence - All messages relating to the persistence process -->
+<!-- DataNucleus.Transaction - All messages relating to transactions -->
+<!-- DataNucleus.Connection - All messages relating to Connections. -->
+<!-- DataNucleus.Query - All messages relating to queries -->
+<!-- DataNucleus.Cache - All messages relating to the DataNucleus Cache -->
+<!-- DataNucleus.MetaData - All messages relating to MetaData -->
+<!-- DataNucleus.Datastore - All general datastore messages -->
+<!-- DataNucleus.Datastore.Schema - All schema related datastore log messages -->
+<!-- DataNucleus.Datastore.Persist - All datastore persistence messages -->
+<!-- DataNucleus.Datastore.Retrieve - All datastore retrieval messages -->
+<!-- DataNucleus.Datastore.Native - Log of all 'native' statements sent to the datastore -->
+<!-- DataNucleus.General - All general operational messages -->
+<!-- DataNucleus.Lifecycle - All messages relating to object lifecycle changes -->
+<!-- DataNucleus.ValueGeneration - All messages relating to value generation -->
+<!-- DataNucleus.Enhancer - All messages from the DataNucleus Enhancer. -->
+<!-- DataNucleus.SchemaTool - All messages from DataNucleus SchemaTool -->
+<!-- DataNucleus.JDO - All messages general to JDO -->
+<!-- DataNucleus.JPA - All messages general to JPA -->
+<!-- DataNucleus.JCA - All messages relating to Connector JCA. -->
+<!-- DataNucleus.IDE - Messages from the DataNucleus IDE. -->
\ No newline at end of file
diff --git a/example/misc/smoketest/logging-integtest.properties b/example/misc/smoketest/logging-integtest.properties
new file mode 100644
index 0000000..a560b72
--- /dev/null
+++ b/example/misc/smoketest/logging-integtest.properties
@@ -0,0 +1,120 @@
+#  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.
+
+
+#
+# Isis uses log4j is used to provide system logging
+#
+log4j.rootCategory=INFO, Console
+
+# The console appender
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.target=System.out
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE}  [%-20c{1} %-10t %-5p]  %m%n
+
+log4j.appender.File=org.apache.log4j.RollingFileAppender
+log4j.appender.File.file=isis.log
+log4j.appender.File.append=false
+log4j.appender.File.layout=org.apache.log4j.PatternLayout
+log4j.appender.File.layout.ConversionPattern=%d [%-20c{1} %-10t %-5p]  %m%n
+
+log4j.appender.translations-po=org.apache.log4j.FileAppender
+log4j.appender.translations-po.File=./translations.pot
+log4j.appender.translations-po.Append=false
+log4j.appender.translations-po.layout=org.apache.log4j.PatternLayout
+log4j.appender.translations-po.layout.ConversionPattern=%m%n
+
+! turn on the internal log4j debugging flag so we can see what it is doing
+#log4j.debug=true
+
+
+# DataNucleus
+# the first two log the DML and DDL (if set to DEBUG)
+log4j.logger.DataNucleus.Datastore.Native=DEBUG, Console
+log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console
+# the remainder can probably be left to WARN
+log4j.logger.DataNucleus.Persistence=DEBUG, Console
+log4j.logger.DataNucleus.Transaction=DEBUG, Console
+log4j.logger.DataNucleus.Connection=WARN, Console
+log4j.logger.DataNucleus.Query=WARN, Console
+log4j.logger.DataNucleus.Cache=WARN, Console
+log4j.logger.DataNucleus.MetaData=WARN, Console
+log4j.logger.DataNucleus.Datastore=WARN, Console
+log4j.logger.DataNucleus.Datastore.Persist=WARN, Console
+log4j.logger.DataNucleus.Datastore.Retrieve=WARN, Console
+log4j.logger.DataNucleus.General=WARN, Console
+log4j.logger.DataNucleus.Lifecycle=WARN, Console
+log4j.logger.DataNucleus.ValueGeneration=WARN, Console
+log4j.logger.DataNucleus.Enhancer=WARN, Console
+log4j.logger.DataNucleus.SchemaTool=ERROR, Console
+log4j.logger.DataNucleus.JDO=WARN, Console
+log4j.logger.DataNucleus.JPA=ERROR, Console
+log4j.logger.DataNucleus.JCA=WARN, Console
+log4j.logger.DataNucleus.IDE=ERROR, Console
+
+log4j.additivity.DataNucleus.Datastore.Native=false
+log4j.additivity.DataNucleus.Datastore.Schema=false
+log4j.additivity.DataNucleus.Datastore.Persistence=false
+log4j.additivity.DataNucleus.Datastore.Transaction=false
+log4j.additivity.DataNucleus.Datastore.Connection=false
+log4j.additivity.DataNucleus.Datastore.Query=false
+log4j.additivity.DataNucleus.Datastore.Cache=false
+log4j.additivity.DataNucleus.Datastore.MetaData=false
+log4j.additivity.DataNucleus.Datastore.Datastore=false
+log4j.additivity.DataNucleus.Datastore.Datastore.Persist=false
+log4j.additivity.DataNucleus.Datastore.Datastore.Retrieve=false
+log4j.additivity.DataNucleus.Datastore.General=false
+log4j.additivity.DataNucleus.Datastore.Lifecycle=false
+log4j.additivity.DataNucleus.Datastore.ValueGeneration=false
+log4j.additivity.DataNucleus.Datastore.Enhancer=false
+log4j.additivity.DataNucleus.Datastore.SchemaTool=false
+log4j.additivity.DataNucleus.Datastore.JDO=false
+log4j.additivity.DataNucleus.Datastore.JPA=false
+log4j.additivity.DataNucleus.Datastore.JCA=false
+log4j.additivity.DataNucleus.Datastore.IDE=false
+
+
+
+
+# if using log4jdbc-remix as JDBC driver
+#log4j.logger.jdbc.sqlonly=DEBUG, sql, Console
+#log4j.additivity.jdbc.sqlonly=false
+#log4j.logger.jdbc.resultsettable=DEBUG, jdbc, Console
+#log4j.additivity.jdbc.resultsettable=false
+
+#log4j.logger.jdbc.audit=WARN,jdbc, Console
+#log4j.additivity.jdbc.audit=false
+#log4j.logger.jdbc.resultset=WARN,jdbc
+#log4j.additivity.jdbc.resultset=false
+#log4j.logger.jdbc.sqltiming=WARN,sqltiming
+#log4j.additivity.jdbc.sqltiming=false
+#log4j.logger.jdbc.connection=FATAL,connection
+#log4j.additivity.jdbc.connection=false
+
+
+log4j.logger.org.apache.isis.core.runtime.services.i18n.po.PoWriter=INFO,translations-po
+log4j.additivity.org.apache.isis.core.runtime.services.i18n.po.PoWriter=false
+
+log4j.logger.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=WARN,Console
+log4j.additivity.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=false
+
+log4j.logger.org.apache.isis.core.integtestsupport.IntegrationBootstrapAbstract=DEBUG,Console
+log4j.additivity.org.apache.isis.core.integtestsupport.IntegrationBootstrapAbstract=false
+
+log4j.logger.com.mchange.v2.c3p0.SQLWarnings=WARN,Console
+log4j.additivity.com.mchange.v2.c3p0.SQLWarnings=false
diff --git a/example/misc/smoketest/pom.xml b/example/misc/smoketest/pom.xml
new file mode 100644
index 0000000..cc7eb79
--- /dev/null
+++ b/example/misc/smoketest/pom.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+        <groupId>org.incode</groupId>
+        <artifactId>incode-build</artifactId>
+        <version>7</version>
+    </parent>
+
+	<groupId>org.apache.isis.example.misc</groupId>
+	<artifactId>smoketest</artifactId>
+
+	<name>Apache Isis - Smoketest</name>
+
+	<properties>
+
+        <revision>2.0.0-M3-SNAPSHOT</revision>
+        <isis.version>${revision}</isis.version>
+
+        <compiler-plugin.source>1.8</compiler-plugin.source>
+        <compiler-plugin.target>1.8</compiler-plugin.target>
+        <compiler-plugin.compilerArgument>-parameters</compiler-plugin.compilerArgument>
+
+        <!-- override incode-build -->
+        <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version>
+        <jetty-maven-plugin.version>9.4.12.v20180830</jetty-maven-plugin.version>
+
+        <!-- override incode-build (see dn5-xxx.version in isis-core-plugins-jdo-datanucleus-5) -->
+        <datanucleus-api-jdo.version>5.2.1</datanucleus-api-jdo.version>
+        <datanucleus-core.version>5.2.1</datanucleus-core.version>
+        <datanucleus-jodatime.version>5.2.0-release</datanucleus-jodatime.version>
+        <hsqldb.version>2.4.1</hsqldb.version>
+
+        <!-- override incode-build -->
+        <maven-surefire-plugin.version>2.22.1</maven-surefire-plugin.version>
+        <maven-surefire-report-plugin.version>2.22.1</maven-surefire-report-plugin.version>
+
+        <failOnMissingWebXml>false</failOnMissingWebXml>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+        <maven-war-plugin.warName>${project.artifactId}</maven-war-plugin.warName>
+	</properties>
+
+	    <build>
+        <resources>
+            <resource>
+                <filtering>true</filtering>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+            </resource>
+            <resource>
+                <filtering>false</filtering>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>com.github.odavid.maven.plugins</groupId>
+                <artifactId>mixin-maven-plugin</artifactId>
+                <version>0.1-alpha-39</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <mixins>
+                        <mixin>
+                            <groupId>com.danhaywood.mavenmixin</groupId>
+                            <artifactId>standard</artifactId>
+                        </mixin>
+                        <mixin>
+                            <groupId>com.danhaywood.mavenmixin</groupId>
+                            <artifactId>datanucleusenhance</artifactId>
+                        </mixin>
+                        <mixin>
+                            <groupId>com.danhaywood.mavenmixin</groupId>
+                            <artifactId>surefire</artifactId>
+                        </mixin>
+                        <mixin>
+                            <groupId>com.danhaywood.mavenmixin</groupId>
+                            <artifactId>jettywar</artifactId>
+                        </mixin>
+                    </mixins>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>1.0.0</version>
+                <configuration>
+                    <updatePomFile>true</updatePomFile>
+                    <pomElements>
+                        <dependencyManagement>resolve</dependencyManagement>
+                        <dependencies>resolve</dependencies>
+                    </pomElements>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>flatten</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.isis.core</groupId>
+                <artifactId>isis</artifactId>
+                <version>${isis.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+
+        <!-- ISIS API -->
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-applib</artifactId>
+        </dependency>
+
+
+        <!-- ISIS CORE PLUGINS -->
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-codegen-bytebuddy</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-discovery-reflections</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-jdo-datanucleus-5</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-eventbus-axon</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-jaxrs-resteasy-4</artifactId>
+        </dependency>
+
+        <!-- OTHER DEPENDENCIES -->
+<!--
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+        </dependency>
+-->
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-webapp</artifactId>
+            <type>pom</type>
+        </dependency>
+
+
+        <!-- TEST DEPENDENCIES -->
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-testing</artifactId>
+            <scope>test</scope>
+            <type>pom</type>
+        </dependency>
+
+    </dependencies>
+
+	<profiles>
+		<profile>
+			<id>m2e</id>
+			<activation>
+				<property>
+					<name>m2e.version</name>
+				</property>
+			</activation>
+			<build>
+				<directory>target-ide</directory>
+				<plugins>
+					<!-- eclipse doesn't seem to support maven mixins config, so repeat 
+						here -->
+					<plugin>
+						<artifactId>maven-compiler-plugin</artifactId>
+						<configuration>
+							<source>${compiler-plugin.source}</source>
+							<target>${compiler-plugin.target}</target>
+							<compilerArgument />
+						</configuration>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Book.java b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Book.java
new file mode 100644
index 0000000..767c817
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Book.java
@@ -0,0 +1,82 @@
+/*
+ *  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 org.apache.isis.testdomain.jdo;
+
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.Discriminator;
+import javax.jdo.annotations.Inheritance;
+import javax.jdo.annotations.InheritanceStrategy;
+import javax.jdo.annotations.PersistenceCapable;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.Publishing;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@PersistenceCapable
+@Inheritance(strategy=InheritanceStrategy.SUPERCLASS_TABLE)
+@Discriminator(value="Book")
+@DomainObject(publishing=Publishing.ENABLED)
+@ToString(callSuper = true)
+public class Book extends Product {
+    
+    public String title() {
+        return toString();
+    }
+    
+    public static Book of(
+            String name, 
+            String description, 
+            double price, 
+            String author, 
+            String isbn, 
+            String publisher) {
+        
+        return new Book(name, description, price, author, isbn, publisher);
+    }
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String author;
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String isbn;
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String publisher;
+
+    // -- CONSTRUCTOR
+    
+    private Book(
+            String name, 
+            String description, 
+            double price, 
+            String author, 
+            String isbn, 
+            String publisher) {
+        
+        super(name, description, price);
+        this.author = author;
+        this.isbn = isbn;
+        this.publisher = publisher;
+    }
+}
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Inventory.java b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Inventory.java
new file mode 100644
index 0000000..6e9b84e
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Inventory.java
@@ -0,0 +1,59 @@
+/*
+ *  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 org.apache.isis.testdomain.jdo;
+
+import java.util.Set;
+
+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.Version;
+import javax.jdo.annotations.VersionStrategy;
+
+import org.apache.isis.applib.annotation.Auditing;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.Property;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+
+@PersistenceCapable(identityType=IdentityType.DATASTORE, schema = "testdomain")
+@DatastoreIdentity(strategy=IdGeneratorStrategy.IDENTITY, column="id")
+@Version(strategy= VersionStrategy.DATE_TIME, column="version")
+@DomainObject(auditing = Auditing.ENABLED)
+@DomainObjectLayout()  // causes UI events to be triggered
+@NoArgsConstructor @AllArgsConstructor(staticName = "of") @ToString
+public class Inventory {
+    
+    public String title() {
+        return toString();
+    }
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String name;
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private Set<Product> products;
+}
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestDomainPersona.java b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestDomainPersona.java
new file mode 100644
index 0000000..3d7fb7e
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestDomainPersona.java
@@ -0,0 +1,100 @@
+/*
+ *  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 org.apache.isis.testdomain.jdo;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.isis.applib.fixturescripts.BuilderScriptAbstract;
+import org.apache.isis.applib.fixturescripts.PersonaWithBuilderScript;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+
+public enum JdoTestDomainPersona 
+implements PersonaWithBuilderScript<BuilderScriptAbstract<Inventory>>  {
+    
+    PurgeAll {
+        @Override
+        public BuilderScriptAbstract<Inventory> builder() {
+            return new BuilderScriptAbstract<Inventory>() {
+
+                @Override
+                protected void execute(ExecutionContext ec) {
+                    
+                    //XXX lombok issue, cannot use val here (https://github.com/rzwitserloot/lombok/issues/434)
+                    RepositoryService repository = IsisContext.getServiceRegistry()
+                            .lookupServiceElseFail(RepositoryService.class);
+                    
+                    repository.allInstances(Inventory.class)
+                    .forEach(repository::remove);
+                    
+                    repository.allInstances(Book.class)
+                    .forEach(repository::remove);
+                    
+                    repository.allInstances(Product.class)
+                    .forEach(repository::remove);
+                    
+                }
+
+                @Override
+                public Inventory getObject() {
+                    return null;
+                }
+                
+            };
+        }    
+    },
+    
+    InventoryWith1Book {
+        @Override
+        public BuilderScriptAbstract<Inventory> builder() {
+            return new BuilderScriptAbstract<Inventory>() {
+
+                private Inventory inventory;
+                
+                @Override
+                protected void execute(ExecutionContext ec) {
+                    
+                    //don't use lombok's val here (https://github.com/rzwitserloot/lombok/issues/434)
+                    RepositoryService repository = IsisContext.getServiceRegistry()
+                            .lookupServiceElseFail(RepositoryService.class);
+                    
+                    Set<Product> products = new HashSet<>();
+                    
+                    products.add(Book.of(
+                            "Sample Book", "A sample book for testing.", 99.,
+                            "Sample Author", "Sample ISBN", "Sample Publisher"));
+                    
+                    inventory = Inventory.of("Sample Inventory", products);
+                    repository.persist(inventory);
+                    
+                }
+
+                @Override
+                public Inventory getObject() {
+                    return inventory;
+                }
+                
+            };
+        }    
+    }
+    
+    ;
+
+    
+    
+}
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestModule.java b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestModule.java
new file mode 100644
index 0000000..bfca145
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/JdoTestModule.java
@@ -0,0 +1,7 @@
+package org.apache.isis.testdomain.jdo;
+
+import org.apache.isis.applib.ModuleAbstract;
+
+public class JdoTestModule extends ModuleAbstract {
+
+}
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Product.java b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Product.java
new file mode 100644
index 0000000..28f120e
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/Product.java
@@ -0,0 +1,63 @@
+/*
+ *  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 org.apache.isis.testdomain.jdo;
+
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.DatastoreIdentity;
+import javax.jdo.annotations.Discriminator;
+import javax.jdo.annotations.DiscriminatorStrategy;
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.Inheritance;
+import javax.jdo.annotations.InheritanceStrategy;
+import javax.jdo.annotations.PersistenceCapable;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Property;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@PersistenceCapable(identityType=IdentityType.DATASTORE, schema = "testdomain")
+@Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
+@Discriminator(strategy=DiscriminatorStrategy.VALUE_MAP, value="Product")
+@DatastoreIdentity(
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.INCREMENT,
+        column="id")
+@DomainObject
+@AllArgsConstructor(access = AccessLevel.PROTECTED) @ToString
+public class Product {
+
+    public String title() {
+        return toString();
+    }
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String name;
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "true")
+    private String description;
+    
+    @Property
+    @Getter @Setter @Column(allowsNull = "false")
+    private double price;
+    
+}
diff --git a/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/isis-non-changing.properties b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/isis-non-changing.properties
new file mode 100644
index 0000000..ae227e4
--- /dev/null
+++ b/example/misc/smoketest/src/main/java/org/apache/isis/testdomain/jdo/isis-non-changing.properties
@@ -0,0 +1,68 @@
+#  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.
+
+isis.reflector.validator.allowDeprecated=false
+isis.reflector.validator.noParamsOnly=true
+isis.reflector.validator.explicitObjectType=true
+isis.reflector.validator.serviceActionsOnly=true
+isis.reflector.validator.mixinsOnly=true
+
+isis.reflector.facet.cssClassFa.patterns=\
+                        new.*:fa-plus,\
+                        add.*:fa-plus-square,\
+                        create.*:fa-plus,\
+                        update.*:fa-edit,\
+                        delete.*:fa-trash,\
+                        find.*:fa-search,\
+                        list.*:fa-list
+
+isis.reflector.facet.cssClass.patterns=\
+                        delete.*:btn-danger
+
+isis.reflector.explicitAnnotations.action=true
+
+isis.objects.editing=false
+
+isis.viewer.wicket.application.name=Hello World App
+isis.viewer.wicket.application.css=css/application.css
+isis.viewer.wicket.application.js=scripts/application.js
+isis.viewer.wicket.application.about=Hello World
+isis.viewer.wicket.welcome.file=welcome.html
+
+isis.viewer.wicket.credit.1.image=/images/apache-isis/logo-48x48.png
+isis.viewer.wicket.credit.1.name=Apache Isis
+isis.viewer.wicket.credit.1.url=http://isis.apache.org
+
+isis.viewer.wicket.themes.initial=Flatly
+isis.viewer.wicket.themes.showChooser=yes
+isis.viewer.wicket.themes.enabled=Cosmo,Flatly,Darkly,Sandstone,United
+
+
+isis.viewer.wicket.maxTitleLengthInStandaloneTables=0
+isis.viewer.wicket.maxTitleLengthInParentedTables=0
+
+
+isis.persistor.datanucleus.impl.datanucleus.schema.autoCreateAll=true
+isis.persistor.datanucleus.impl.datanucleus.schema.validateTables=true
+isis.persistor.datanucleus.impl.datanucleus.schema.validateConstraints=true
+
+
+isis.persistor.datanucleus.impl.datanucleus.persistenceByReachabilityAtCommit=false
+isis.persistor.datanucleus.impl.datanucleus.identifier.case=MixedCase
+isis.persistor.datanucleus.impl.datanucleus.cache.level2.type=none
+isis.persistor.datanucleus.impl.datanucleus.cache.level2.mode=ENABLE_SELECTIVE
+
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/AuditerServiceTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/AuditerServiceTest.java
new file mode 100644
index 0000000..19a2385
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/AuditerServiceTest.java
@@ -0,0 +1,130 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.fixturescripts.FixtureScripts;
+import org.apache.isis.applib.services.audit.AuditerService;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.testdomain.jdo.Book;
+import org.apache.isis.testdomain.jdo.JdoTestDomainPersona;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import lombok.val;
+
+/**
+ * Depends on {@link JdoBootstrappingTest} to succeed.
+ */
+@Disabled //TODO[2112] activate tests
+class AuditerServiceTest {
+    
+    @Inject FixtureScripts fixtureScripts;
+
+    @BeforeEach
+    void setUp() {
+        
+        // cleanup
+        fixtureScripts.runBuilderScript(
+                JdoTestDomainPersona.PurgeAll.builder());
+        
+        // given
+        fixtureScripts.runBuilderScript(
+                JdoTestDomainPersona.InventoryWith1Book.builder());
+    }
+    
+    @Test
+    void auditerServiceShouldBeAwareOfInventoryChanges() {
+        
+        val auditerServiceAny = IsisContext.getServiceRegistry()
+                .lookupServiceElseFail(AuditerService.class);
+        
+        // priority based service lookup (could be a test on its own)
+        assertEquals(AuditerServiceStub.class, auditerServiceAny.getClass());
+        
+
+        // given
+        val auditerService = (AuditerServiceStub) auditerServiceAny; 
+        val repository = IsisContext.getServiceRegistry()
+                .lookupServiceElseFail(RepositoryService.class);
+        val txMan = IsisContext.getPersistenceSession().get().getTransactionManager();
+        val book = repository.allInstances(Book.class).listIterator().next();
+        
+        // when
+        auditerService.clearHistory();
+        book.setName("Book #2");
+        repository.persist(book);
+        txMan.endTransaction();
+        
+        // then 
+        assertEquals(
+                "targetClassName=Book,propertyName=name,preValue=Sample Book,postValue=Book #2;", 
+                auditerService.getHistory());
+    }
+    
+    // -- HELPER
+        
+    @DomainService(menuOrder="1.0", nature=NatureOfService.DOMAIN)
+    @DomainServiceLayout()
+    public static class AuditerServiceStub implements AuditerService {
+
+        private StringBuilder history = new StringBuilder();
+        
+        @Override
+        public boolean isEnabled() {
+            return true;
+        }
+
+        @Override
+        public void audit(
+                UUID interactionId, int sequence, String targetClassName, Bookmark target,
+                String memberIdentifier, String propertyName, 
+                String preValue, String postValue, String user,
+                Timestamp timestamp) {
+            
+            history
+            .append("targetClassName=").append(targetClassName).append(",")
+            .append("propertyName=").append(propertyName).append(",")
+            .append("preValue=").append(preValue).append(",")
+            .append("postValue=").append(postValue).append(";")
+            ;
+        }
+        
+        void clearHistory() {
+            history = new StringBuilder();
+        }
+        
+        String getHistory() {
+            return history.toString();
+        }
+
+    }
+    
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/H2Console.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/H2Console.java
new file mode 100644
index 0000000..0b66e3f
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/H2Console.java
@@ -0,0 +1,11 @@
+package org.apache.isis.testdomain.tests;
+
+import java.sql.SQLException;
+
+public class H2Console {
+
+	public static void main(String[] args) throws SQLException {
+		org.h2.tools.Console.main(args);
+	}
+	
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoBootstrappingTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoBootstrappingTest.java
new file mode 100644
index 0000000..a21eb64
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoBootstrappingTest.java
@@ -0,0 +1,134 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.fixturescripts.FixtureScripts;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.core.commons.collections.Bin;
+import org.apache.isis.core.integtestsupport.IntegrationTestJupiter;
+import org.apache.isis.core.runtime.headless.HeadlessTransactionSupport;
+import org.apache.isis.core.runtime.system.internal.InitialisationSession;
+import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
+import org.apache.isis.testdomain.jdo.Inventory;
+import org.apache.isis.testdomain.jdo.JdoTestDomainPersona;
+import org.apache.isis.testdomain.jdo.JdoTestModule;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import lombok.val;
+
+class JdoBootstrappingTest extends IntegrationTestJupiter {
+
+    public JdoBootstrappingTest() {
+        super(new JdoTestModule()
+                
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.cache.level2.mode", "ENABLE_SELECTIVE")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.cache.level2.type", "none")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.identifier.case", "MixedCase")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.persistenceByReachabilityAtCommit", "false")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.schema.autoCreateAll", "true")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.schema.validateConstraints", "true")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.schema.validateTables", "true")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionDriverName", "org.h2.Driver")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionPassword", "")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionURL", "jdbc:h2:mem:test")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.javax.jdo.option.ConnectionUserName", "sa")
+                .withConfigurationProperty("isis.reflector.explicitAnnotations.action", "true")
+                .withConfigurationProperty("isis.reflector.facet.cssClass.patterns", "delete.*:btn-danger")
+                .withConfigurationProperty("isis.reflector.facet.cssClassFa.patterns", "new.*:fa-plus,add.*:fa-plus-square,create.*:fa-plus,update.*:fa-edit,delete.*:fa-trash,find.*:fa-search,list.*:fa-list")
+                .withConfigurationProperty("isis.reflector.validator.allowDeprecated", "false")
+                .withConfigurationProperty("isis.reflector.validator.explicitObjectType", "true")
+                .withConfigurationProperty("isis.reflector.validator.mixinsOnly", "true")
+                .withConfigurationProperty("isis.reflector.validator.noParamsOnly", "true")
+                .withConfigurationProperty("isis.reflector.validator.serviceActionsOnly", "true")
+                .withConfigurationProperty("isis.services.translation.po.mode", "disable")
+                
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.schema.autoCreateAll","true")
+                .withConfigurationProperty("isis.persistor.datanucleus.impl.datanucleus.identifier.case","MixedCase")
+                .withConfigurationProperty("isis.objects.editing", "false"));
+    }
+
+    @Inject IsisSessionFactory isisSessionFactory;
+    @Inject HeadlessTransactionSupport transactions;
+    @Inject FixtureScripts fixtureScripts;
+    @Inject RepositoryService repository;
+    
+    @BeforeEach
+    void beforeEach() {
+        System.out.println("================== START ====================");
+    }
+    
+    void setUp() {
+        
+//        isisSessionFactory.openSession(new InitialisationSession());
+//        
+//        transactions.beginTransaction();
+        
+        System.out.println("================== RUN FIXTURE 1 ====================");
+        
+//        // cleanup
+//        fixtureScripts.runBuilderScript(
+//                JdoTestDomainPersona.PurgeAll.builder());
+        
+        System.out.println("================== RUN FIXTURE 2 ====================");
+
+        // given
+        fixtureScripts.runBuilderScript(
+                JdoTestDomainPersona.InventoryWith1Book.builder());
+        
+        System.out.println("================== FIXTURES DONE ====================");
+        
+//        transactions.endTransaction();
+//        
+//        isisSessionFactory.closeSession();
+
+    }
+    
+    @Test
+    void sampleInventoryShouldBeSetUp() {
+        
+        setUp();
+
+//    	isisSessionFactory.openSession(new InitialisationSession());
+//    	
+//    	transactions.beginTransaction();
+    	
+    	val inventories = Bin.ofCollection(repository.allInstances(Inventory.class)); 
+		assertEquals(1, inventories.size());
+		
+		val inventory = inventories.getSingleton().get();
+        assertNotNull(inventory);
+        assertNotNull(inventory.getProducts());
+        assertEquals(1, inventory.getProducts().size());
+        
+        val product = inventory.getProducts().iterator().next();
+        assertEquals("Sample Book", product.getName());
+        
+//        transactions.endTransaction();
+//        
+//        isisSessionFactory.closeSession();
+        
+    }
+
+        
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoTransactionTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoTransactionTest.java
new file mode 100644
index 0000000..28adf3d
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/JdoTransactionTest.java
@@ -0,0 +1,124 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.fixturescripts.FixtureScripts;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.core.commons.collections.Bin;
+import org.apache.isis.core.runtime.headless.HeadlessTransactionSupport;
+import org.apache.isis.core.runtime.system.internal.InitialisationSession;
+import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
+import org.apache.isis.testdomain.jdo.Book;
+import org.apache.isis.testdomain.jdo.Inventory;
+import org.apache.isis.testdomain.jdo.JdoTestDomainPersona;
+import org.apache.isis.testdomain.jdo.Product;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import lombok.val;
+
+class JdoTransactionTest {
+
+	@Inject IsisSessionFactory isisSessionFactory;
+	@Inject HeadlessTransactionSupport transactions;
+	@Inject FixtureScripts fixtureScripts;
+
+	@Inject RepositoryService repository;
+
+	@BeforeAll
+	static void launchH2Console() throws SQLException {
+		H2Console.main(null);
+	}
+	
+	@BeforeEach
+	void setUp() {
+
+		isisSessionFactory.openSession(new InitialisationSession());
+
+		transactions.beginTransaction();
+
+		// cleanup
+		fixtureScripts.runBuilderScript(
+				JdoTestDomainPersona.PurgeAll.builder());
+
+		transactions.endTransaction();
+
+		isisSessionFactory.closeSession();
+	}
+
+	@Test
+	void sampleInventoryShouldBeSetUp() {
+
+		// expected pre condition: no inventories
+		
+		isisSessionFactory.openSession(new InitialisationSession());
+		assertEquals(0, repository.allInstances(Inventory.class).size());
+		isisSessionFactory.closeSession();
+
+		{ // setup sample Inventory
+		
+			isisSessionFactory.openSession(new InitialisationSession());
+	
+			transactions.beginTransaction();
+	
+			Set<Product> products = new HashSet<>();
+	
+			products.add(Book.of(
+					"Sample Book", "A sample book for testing.", 99.,
+					"Sample Author", "Sample ISBN", "Sample Publisher"));
+	
+			val inventory = Inventory.of("Sample Inventory", products);
+			repository.persist(inventory);
+	
+			transactions.endTransaction();
+	
+			isisSessionFactory.closeSession();
+		
+		}
+
+		// expected post condition: ONE inventory
+
+		isisSessionFactory.openSession(new InitialisationSession());
+		
+		val inventories = Bin.ofCollection(repository.allInstances(Inventory.class)); 
+		assertEquals(1, inventories.size());
+		
+		val inventory = inventories.getSingleton().get();
+		assertNotNull(inventory);
+		assertNotNull(inventory.getProducts());
+		assertEquals(1, inventory.getProducts().size());
+
+		val product = inventory.getProducts().iterator().next();
+		assertEquals("Sample Book", product.getName());
+		
+		isisSessionFactory.closeSession();
+
+	}
+
+
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/PublisherServiceTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/PublisherServiceTest.java
new file mode 100644
index 0000000..ba9bb92
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/PublisherServiceTest.java
@@ -0,0 +1,129 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.fixturescripts.FixtureScripts;
+import org.apache.isis.applib.services.iactn.Interaction.Execution;
+import org.apache.isis.applib.services.publish.PublishedObjects;
+import org.apache.isis.applib.services.publish.PublisherService;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.apache.isis.testdomain.jdo.Book;
+import org.apache.isis.testdomain.jdo.JdoTestDomainPersona;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import lombok.val;
+
+/**
+ * Depends on {@link JdoBootstrappingTest} to succeed.
+ */
+class PublisherServiceTest  {
+    
+    @Inject FixtureScripts fixtureScripts;
+
+    @BeforeEach
+    void setUp() {
+        
+        // cleanup
+        fixtureScripts.runBuilderScript(
+                JdoTestDomainPersona.PurgeAll.builder());
+        
+        // given
+        fixtureScripts.runBuilderScript(
+                JdoTestDomainPersona.InventoryWith1Book.builder());
+    }
+    
+    @Test @Disabled("service lookup fails")
+    void publisherServiceShouldBeAwareOfInventoryChanges() {
+        
+        
+        val serviceRegistry = IsisContext.getServiceRegistry();
+        
+        Map<Class<? extends PublisherService>, PublisherService> map =
+                _NullSafe.streamNullable(serviceRegistry.lookupService(PublisherService.class).orElse(null))
+                .collect(Collectors.toMap(ps->ps.getClass(), ps->ps));
+        
+        // TODO any instance type lookup (should be a test on its own)
+        assertTrue(map.containsKey(PublisherServiceStub.class));
+        
+        // priority based service lookup (could be a test on its own)
+        val publisherService = (PublisherServiceStub) map.get(PublisherServiceStub.class);
+        
+        // given
+//        val publisherService = (PublisherServiceStub) publisherServiceAny; 
+        val repository = IsisContext.getServiceRegistry()
+                .lookupServiceElseFail(RepositoryService.class);
+        val txMan = IsisContext.getPersistenceSession().get().getTransactionManager();
+        val book = repository.allInstances(Book.class).listIterator().next();
+        
+        // when
+        publisherService.clearHistory();
+        book.setName("Book #2");
+        repository.persist(book);
+        txMan.endTransaction();
+        
+        // then TODO
+//        assertEquals(
+//                "...", 
+//                publisherService.getHistory());
+    }
+    
+    // -- HELPER
+        
+    @DomainService(menuOrder="1.0", nature=NatureOfService.DOMAIN)
+    @DomainServiceLayout()
+    public static class PublisherServiceStub implements PublisherService {
+
+        private StringBuilder history = new StringBuilder();
+        
+        void clearHistory() {
+            history = new StringBuilder();
+        }
+        
+        String getHistory() {
+            return history.toString();
+        }
+
+        @Override
+        public void publish(Execution<?, ?> execution) {
+            history.append("execution=").append(execution).append(",");
+            System.out.println("h1: " + history);
+        }
+
+        @Override
+        public void publish(PublishedObjects publishedObjects) {
+            history.append("publishedObjects=").append(publishedObjects).append(",");
+            System.out.println("h2: " + history);
+        }
+
+    }
+    
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringEventPublishingTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringEventPublishingTest.java
new file mode 100644
index 0000000..0badd70
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringEventPublishingTest.java
@@ -0,0 +1,87 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import lombok.Getter;
+import lombok.Value;
+import lombok.val;
+
+class SpringEventPublishingTest {
+
+    @Inject TestPublisher testPublisher;
+    @Inject TestListener testListener;
+    
+    @Test
+    void firedEventShouldBeReceivedImmediately() {
+        
+        val history = testListener.getHistory();
+        
+        testPublisher.fireHelloWorld();
+        
+        assertEquals("Hello World!", history.toString());
+    }
+    
+    // -- HELPER
+    
+    //@Configuration
+    static class TestConfig {
+    
+        //@Bean
+//        Event<GenericSpringEvent<String>> createEvent(ApplicationEventPublisher publisher) {
+//            return _Spring.event(publisher);
+//        }
+        
+    }
+    
+    @Singleton
+    public static class TestPublisher {
+        
+        @Inject Event<GenericSpringEvent<String>> genericEvent;
+        
+        public void fireHelloWorld() {
+            genericEvent.fire(GenericSpringEvent.of("Hello World!"));
+        }
+        
+    }
+    
+    @Singleton
+    public static class TestListener {
+        
+        @Getter private final StringBuilder history = new StringBuilder();
+
+        //@EventListener(GenericSpringEvent.class)
+        public void receiveHelloWorld(@Observes GenericSpringEvent<String> event) {
+            history.append(event.what);
+        }
+        
+    }
+    
+    @Value(staticConstructor = "of")
+    static class GenericSpringEvent<T> {
+        private T what;
+    }
+        
+}
diff --git a/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringServiceProvisioningTest.java b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringServiceProvisioningTest.java
new file mode 100644
index 0000000..a8e4188
--- /dev/null
+++ b/example/misc/smoketest/src/test/java/org/apache/isis/testdomain/tests/SpringServiceProvisioningTest.java
@@ -0,0 +1,63 @@
+/*
+ *  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 org.apache.isis.testdomain.tests;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.commons.internal.resources._Json;
+import org.apache.isis.commons.internal.resources._Resources;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+
+import static org.apache.isis.commons.internal.collections._Collections.toStringJoiningNewLine;
+import static org.apache.isis.commons.internal.collections._Sets.intersectSorted;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class SpringServiceProvisioningTest {
+
+    @BeforeEach
+    void setUp() {
+     
+    }
+    
+    @Test
+    void builtInServicesShouldBeSetUp() throws IOException {
+        
+        SortedSet<String> managedServices = IsisContext.getServiceRegistry().streamServiceTypes()
+                .map(Class::getName)
+                .collect(Collectors.toCollection(TreeSet::new));
+        
+        String singletonJson = _Resources.loadAsString(this.getClass(), "builtin-IsisBoot.json", StandardCharsets.UTF_8);
+        SortedSet<String> singletonSet = new TreeSet<>(_Json.readJsonList(String.class, singletonJson));
+        
+        // same as managedServices.containsAll(singletonSet) but more verbose in case of failure        
+        assertEquals(
+                toStringJoiningNewLine(singletonSet), 
+                toStringJoiningNewLine(intersectSorted(managedServices, singletonSet)));
+        
+        //TODO also test for request-scoped service (requires a means to mock a request-context)
+        
+    }
+ 
+        
+}
diff --git a/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-IsisBoot.json b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-IsisBoot.json
new file mode 100644
index 0000000..3e92663
--- /dev/null
+++ b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-IsisBoot.json
@@ -0,0 +1,30 @@
+[
+  "org.apache.isis.applib.services.clock.ClockService",
+  "org.apache.isis.applib.services.jaxb.JaxbServiceDefault",
+  "org.apache.isis.applib.services.publish.PublisherServiceLogging",
+  "org.apache.isis.applib.services.queryresultscache.QueryResultsCacheControlInternal",
+  "org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureFactory",
+  "org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureRepositoryDefault",
+  "org.apache.isis.core.metamodel.services.exceprecog.ExceptionRecognizerDocDefault",
+  "org.apache.isis.core.metamodel.services.grid.GridLoaderServiceDefault",
+  "org.apache.isis.core.metamodel.services.grid.GridServiceDefault",
+  "org.apache.isis.core.metamodel.services.grid.bootstrap3.GridSystemServiceBS3",
+  "org.apache.isis.core.metamodel.services.layout.LayoutServiceDefault",
+  "org.apache.isis.core.metamodel.services.metamodel.MetaModelServiceDefault",
+  "org.apache.isis.core.metamodel.services.registry.ServiceRegistryDefault",
+  "org.apache.isis.core.metamodel.services.repository.RepositoryServiceInternalDefault",
+  "org.apache.isis.core.metamodel.services.swagger.SwaggerServiceDefault",
+  "org.apache.isis.core.metamodel.services.title.TitleServiceDefault",
+  "org.apache.isis.core.metamodel.services.user.UserServiceDefault",
+  "org.apache.isis.core.runtime.services.authsess.AuthenticationSessionProviderDefault",
+  "org.apache.isis.core.runtime.services.command.CommandServiceDefault",
+  "org.apache.isis.core.runtime.services.homepage.HomePageProviderServiceDefault",
+  "org.apache.isis.core.runtime.services.i18n.po.TranslationServicePo",
+  "org.apache.isis.core.runtime.services.menubars.MenuBarsLoaderServiceDefault",
+  "org.apache.isis.core.runtime.services.menubars.bootstrap3.MenuBarsServiceBS3",
+  "org.apache.isis.core.runtime.services.message.MessageServiceDefault",
+  "org.apache.isis.core.runtime.services.xactn.TransactionServiceDefault",
+  "org.apache.isis.core.runtime.services.xmlsnapshot.XmlSnapshotServiceDefault",
+  "org.apache.isis.core.security.authentication.standard.AuthenticationManagerStandard",
+  "org.apache.isis.core.wrapper.WrapperFactoryDefault"
+ ]
\ No newline at end of file
diff --git a/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-requestscoped.json b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-requestscoped.json
new file mode 100644
index 0000000..a4f90ff
--- /dev/null
+++ b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-requestscoped.json
@@ -0,0 +1,10 @@
+[
+ "org.apache.isis.applib.services.command.CommandContext",
+ "org.apache.isis.applib.services.iactn.InteractionContext",
+ "org.apache.isis.applib.services.queryresultscache.QueryResultsCacheInternal",
+ "org.apache.isis.applib.services.scratchpad.Scratchpad",
+ "org.apache.isis.core.runtime.services.changes.ChangedObjectsServiceInternal",
+ "org.apache.isis.core.runtime.services.metrics.MetricsServiceDefault",
+ "org.apache.isis.core.runtime.services.publish.PublishingServiceInternalDefault",
+ "org.apache.isis.objectstore.jdo.datanucleus.service.support.TimestampService"
+]
diff --git a/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-singleton.json b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-singleton.json
new file mode 100644
index 0000000..5908c85
--- /dev/null
+++ b/example/misc/smoketest/src/test/resources/org/apache/isis/testdomain/tests/builtin-singleton.json
@@ -0,0 +1,75 @@
+[
+ "org.apache.isis.applib.conmap.ContentMappingServiceForCommandDto",
+ "org.apache.isis.applib.conmap.ContentMappingServiceForCommandDto$CopyOverFromCommand",
+ "org.apache.isis.applib.conmap.ContentMappingServiceForCommandsDto",
+ "org.apache.isis.applib.fixturescripts.ExecutionParametersService",
+ "org.apache.isis.applib.mixins.dto.DtoMappingHelper",
+ "org.apache.isis.applib.services.audit.AuditerServiceLogging",
+ "org.apache.isis.applib.services.classdiscovery.ClassDiscoveryServiceDefault",
+ "org.apache.isis.applib.services.clock.ClockService",
+ "org.apache.isis.applib.services.confview.ConfigurationMenu",
+ "org.apache.isis.applib.services.exceprecog.jdo.ExceptionRecognizerCompositeForJdoObjectStore",
+ "org.apache.isis.applib.services.fixturespec.FixtureScriptsDefault",
+ "org.apache.isis.applib.services.h2.H2ManagerMenu",
+ "org.apache.isis.applib.services.hsqldb.HsqlDbManagerMenu",
+ "org.apache.isis.applib.services.jaxb.JaxbServiceDefault",
+ "org.apache.isis.applib.services.jdosupport.IsisJdoSupportDN5",
+ "org.apache.isis.applib.services.layout.LayoutServiceMenu",
+ "org.apache.isis.applib.services.metamodel.MetaModelServicesMenu",
+ "org.apache.isis.applib.services.publish.PublisherServiceLogging",
+ "org.apache.isis.applib.services.queryresultscache.QueryResultsCacheControlInternal",
+ "org.apache.isis.applib.services.routing.RoutingServiceDefault",
+ "org.apache.isis.applib.services.swagger.SwaggerServiceMenu",
+ "org.apache.isis.applib.services.tablecol.TableColumnOrderService$Default",
+ "org.apache.isis.applib.services.urlencoding.UrlEncodingServiceWithCompression",
+ "org.apache.isis.config.builder.IsisConfigurationDefault",
+ "org.apache.isis.config.services.view.ConfigurationViewServiceDefault",
+ "org.apache.isis.core.integtestsupport.components.HeadlessTransactionSupportDefault",
+ "org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureFactory",
+ "org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureRepositoryDefault",
+ "org.apache.isis.core.metamodel.services.bookmarks.BookmarkServiceInternalDefault",
+ "org.apache.isis.core.metamodel.services.exceprecog.ExceptionRecognizerDocDefault",
+ "org.apache.isis.core.metamodel.services.factory.FactoryServiceInternalDefault",
+ "org.apache.isis.core.metamodel.services.grid.GridLoaderServiceDefault",
+ "org.apache.isis.core.metamodel.services.grid.GridServiceDefault",
+ "org.apache.isis.core.metamodel.services.grid.bootstrap3.GridSystemServiceBS3",
+ "org.apache.isis.core.metamodel.services.layout.LayoutServiceDefault",
+ "org.apache.isis.core.metamodel.services.message.MessageServiceNoop",
+ "org.apache.isis.core.metamodel.services.metamodel.MetaModelServiceDefault",
+ "org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternalNoop",
+ "org.apache.isis.core.metamodel.services.registry.ServiceRegistryDefault",
+ "org.apache.isis.core.metamodel.services.repository.RepositoryServiceInternalDefault",
+ "org.apache.isis.core.metamodel.services.swagger.SwaggerServiceDefault",
+ "org.apache.isis.core.metamodel.services.title.TitleServiceDefault",
+ "org.apache.isis.core.metamodel.services.user.UserServiceDefault",
+ "org.apache.isis.core.metamodel.services.user.UserServiceDefault$SudoServiceSpi",
+ "org.apache.isis.core.metamodel.specloader.SpecificationLoader",
+ "org.apache.isis.core.runtime.authorization.standard.AuthorizationManagerStandard",
+ "org.apache.isis.core.runtime.services.auditing.AuditingServiceInternal",
+ "org.apache.isis.core.runtime.services.authsess.AuthenticationSessionProviderDefault",
+ "org.apache.isis.core.runtime.services.background.BackgroundServiceDefault",
+ "org.apache.isis.core.runtime.services.background.CommandExecutorServiceDefault",
+ "org.apache.isis.core.runtime.services.command.CommandDtoServiceInternalDefault",
+ "org.apache.isis.core.runtime.services.command.CommandServiceDefault",
+ "org.apache.isis.core.runtime.services.email.EmailServiceDefault",
+ "org.apache.isis.core.runtime.services.homepage.HomePageProviderServiceDefault",
+ "org.apache.isis.core.runtime.services.i18n.po.TranslationServicePo",
+ "org.apache.isis.core.runtime.services.i18n.po.TranslationServicePoMenu",
+ "org.apache.isis.core.runtime.services.ixn.InteractionDtoServiceInternalDefault",
+ "org.apache.isis.core.runtime.services.menubars.MenuBarsLoaderServiceDefault",
+ "org.apache.isis.core.runtime.services.menubars.bootstrap3.MenuBarsServiceBS3",
+ "org.apache.isis.core.runtime.services.message.MessageServiceDefault",
+ "org.apache.isis.core.runtime.services.persistsession.PersistenceSessionServiceInternalDefault",
+ "org.apache.isis.core.runtime.services.sessmgmt.SessionManagementServiceDefault",
+ "org.apache.isis.core.runtime.services.sse.EventStreamServiceDefault",
+ "org.apache.isis.core.runtime.services.sudo.SudoServiceDefault",
+ "org.apache.isis.core.runtime.services.userprof.UserProfileServiceDefault",
+ "org.apache.isis.core.runtime.services.userreg.EmailNotificationServiceDefault",
+ "org.apache.isis.core.runtime.services.xactn.TransactionServiceDefault",
+ "org.apache.isis.core.runtime.services.xmlsnapshot.XmlSnapshotServiceDefault",
+ "org.apache.isis.core.runtime.system.persistence.PersistenceSessionFactory5",
+ "org.apache.isis.core.runtime.system.session.IsisSessionFactory",
+ "org.apache.isis.core.security.authentication.standard.AuthenticationManagerStandard",
+ "org.apache.isis.core.wrapper.WrapperFactoryDefault",
+ "org.apache.isis.objectstore.jdo.datanucleus.service.eventbus.EventBusServiceJdo"
+]
diff --git a/example/misc/smoketest/translations.pot b/example/misc/smoketest/translations.pot
new file mode 100644
index 0000000..e69de29