You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/09/28 05:33:40 UTC

[isis-app-demo] 01/01: copies 2.0.0-M6 version of simpleapp

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

danhaywood pushed a commit to tag tags/01-01-starter-app
in repository https://gitbox.apache.org/repos/asf/isis-app-demo.git

commit 0026087a655ff0865e532ea68adfbdadcf12c592
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Sep 28 06:28:50 2021 +0100

    copies 2.0.0-M6 version of simpleapp
---
 .gitattributes                                     |  51 +++++
 .github/workflows/build-and-test.yml               | 100 +++++++++
 .gitignore                                         |  45 ++++
 LICENSE                                            | 201 +++++++++++++++++
 README.adoc                                        | 244 +++++++++++++++++++++
 lib/README.txt                                     |   3 +
 lib/spring-instrument-5.3.8.jar                    | Bin 0 -> 7429 bytes
 module-simple-tests/.gitignore                     |   8 +
 module-simple-tests/log4j2-test.xml                |  60 +++++
 module-simple-tests/pom.xml                        |  69 ++++++
 .../modules/simple/dom/so/SimpleObject_Test.java   |  71 ++++++
 .../modules/simple/dom/so/SimpleObjects_Test.java  |  80 +++++++
 .../integtests/SimpleModuleIntegTestAbstract.java  |  50 +++++
 .../integtests/tests/SimpleObject_IntegTest.java   |  85 +++++++
 .../integtests/tests/SimpleObjects_IntegTest.java  |  95 ++++++++
 .../src/test/resources/application-test.yml        |   4 +
 module-simple/.gitignore                           |   8 +
 module-simple/pom.xml                              |  62 ++++++
 .../domainapp/modules/simple/SimpleModule.java     |  28 +++
 .../dom/so/SimpleObject#others.columnOrder.txt     |   2 +
 .../simple/dom/so/SimpleObject.columnOrder.txt     |   2 +
 .../modules/simple/dom/so/SimpleObject.java        | 140 ++++++++++++
 .../modules/simple/dom/so/SimpleObject.layout.xml  |  42 ++++
 .../modules/simple/dom/so/SimpleObject.png         | Bin 0 -> 653 bytes
 .../simple/dom/so/SimpleObjectRepository.java      |  13 ++
 .../modules/simple/dom/so/SimpleObjects.java       |  92 ++++++++
 .../simple/fixture/SimpleObjectBuilder.java        |  31 +++
 .../simple/fixture/SimpleObject_persona.java       |  47 ++++
 .../java/domainapp/modules/simple/types/Name.java  |  20 ++
 .../java/domainapp/modules/simple/types/Notes.java |  25 +++
 pom.xml                                            | 107 +++++++++
 update-parent.sh                                   |  15 ++
 webapp-tests/log4j2-test.xml                       |  70 ++++++
 webapp-tests/pom.xml                               |  54 +++++
 .../webapp/integtests/WebAppIntegTestAbstract.java |  55 +++++
 .../metamodel/SwaggerExport_IntegTest.java         |  34 +++
 .../metamodel/ValidateDomainModel_IntegTest.java   |  22 ++
 .../webapp/integtests/smoke/Smoke_IntegTest.java   |  92 ++++++++
 .../src/test/resources/application-test.yml        |   4 +
 .../src/test/resources/junit-platform.properties   |  17 ++
 webapp/pom.xml                                     | 196 +++++++++++++++++
 .../main/java/domainapp/webapp/AppManifest.java    |  47 ++++
 .../src/main/java/domainapp/webapp/SimpleApp.java  |  26 +++
 .../webapp/application/ApplicationModule.java      |  14 ++
 .../fixture/scenarios/DomainAppDemo.java           |  20 ++
 .../services/health/HealthCheckServiceImpl.java    |  33 +++
 .../services/homepage/HomePageViewModel.java       |  32 +++
 .../services/homepage/HomePageViewModel.layout.xml |  38 ++++
 .../services/homepage/HomePageViewModel.png        | Bin 0 -> 456 bytes
 .../java/domainapp/webapp/custom/CustomModule.java |  10 +
 .../webapp/custom/restapi/CustomController.java    |  47 ++++
 .../java/domainapp/webapp/quartz/QuartzModule.java |  41 ++++
 .../domainapp/webapp/quartz/job/SampleJob.java     |  55 +++++
 webapp/src/main/resources/application.yml          | 106 +++++++++
 webapp/src/main/resources/banner.txt               |   6 +
 .../config/application-SQLSERVER.properties        |  31 +++
 .../main/resources/config/application.properties   |  31 +++
 .../V2020.01.20.14.10__create_schema_simple.sql    |   8 +
 ....20.14.14__create_table_simple_SimpleObject.sql |  25 +++
 webapp/src/main/resources/log4j2-spring.xml        |  64 ++++++
 webapp/src/main/resources/menubars.layout.xml      |  82 +++++++
 webapp/src/main/resources/shiro.ini                |  53 +++++
 .../src/main/resources/static/css/application.css  |   0
 webapp/src/main/resources/static/css/page.css      |  27 +++
 .../static/images/apache-isis/logo-48x48.png       | Bin 0 -> 2622 bytes
 .../resources/static/images/apache-isis/logo.png   | Bin 0 -> 14160 bytes
 .../src/main/resources/static/images/favicon.png   | Bin 0 -> 2143 bytes
 webapp/src/main/resources/static/index.html        |  54 +++++
 .../main/resources/static/scripts/application.js   |   3 +
 webapp/src/main/resources/templates/error.html     |  20 ++
 webapp/translations.po                             |   0
 71 files changed, 3217 insertions(+)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..b1eafb6
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,51 @@
+#
+#
+# text files are normalized (convert crlf => lf)
+# binary files are not normalized (binary is a macro for -text -diff)
+#
+#
+
+
+# Unless otherwise stated, assume text
+
+* text=auto
+
+
+*.java text diff=java
+*.html text diff=html
+*.xhtml text diff=html
+*.xml text
+*.txt text
+
+
+*.jar binary
+*.so binary
+*.dll binary
+
+# images
+*.jpg binary
+*.jpeg binary
+*.png binary
+*.pdn binary
+*.pdn binary
+
+
+*.cs     text diff=csharp
+
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain
+
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 0000000..780b239
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,100 @@
+name: Maven Build and Test
+
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - jdo
+      - jpa
+      - jdo-SNAPSHOT
+      - jpa-SNAPSHOT
+  pull_request:
+    branches:
+      - jdo
+      - jpa
+      - jdo-SNAPSHOT
+      - jpa-SNAPSHOT
+
+jobs:
+  build:
+    name: Build, Test
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        java-version:
+          - 11
+          - 15
+
+    steps:
+    - name: Checkout repo
+      uses: actions/checkout@v2.3.3
+
+    - name: Set up JDK ${{ matrix.java-version }}
+      uses: actions/setup-java@v1
+      with:
+        java-version: ${{ matrix.java-version }}
+
+    - name: Print Maven Version
+      run: mvn --version
+
+    - name: Activate Cache for Maven Downloads
+      uses: actions/cache@v2
+      env:
+        cache-name: cache-maven-modules
+      with:
+        path: ~/.m2/repository
+        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pom.xml') }}
+        restore-keys: |
+          ${{ runner.os }}-build-${{ env.cache-name }}-
+          ${{ runner.os }}-build-
+          ${{ runner.os }}-
+
+    - name: Calculate $REVISION
+      id: revision
+      shell: bash
+      run: |
+        if [[ $GITHUB_REF =~ .*-SNAPSHOT ]]
+        then
+          PARENT=$(curl -X GET "https://nexus.incode.work/service/rest/v1/search?sort=version&repository=nightly-builds&group=org.apache.isis.app&name=isis-app-starter-parent" -H "accept: application/json" -s | jq '.items[0].version' | sed 's/"//g')
+          echo "parentVersion = $PARENT"
+          mvn versions:update-parent -DparentVersion="[$PARENT]"
+        fi
+        BASELINE=$(cat pom.xml | grep "<version>" | head -1 | awk -F'>' '{print $2}' | awk -F'<' '{print $1}')
+        SHA_ID=$(echo $GITHUB_SHA | cut -c1-8)
+        REVISION=${BASELINE}.$(date +%Y%m%d-%H%M)-${SHA_ID}
+        echo "##[set-output name=revision;]${REVISION}"
+
+    - name: Share $REVISION as Environment Variable
+      run: echo "REVISION=${{ steps.revision.outputs.revision }}" >> $GITHUB_ENV
+
+    - name: Print $REVISION
+      shell: bash
+      run: |
+        echo ==============  ENV  =================
+        echo REVISION            \: $REVISION
+        echo ======================================
+
+    - name: Update Maven version
+      shell: bash
+      run: |
+        mvn versions:set -DnewVersion=$REVISION
+
+    - name: Print pom.xml
+      shell: bash
+      run: |
+        cat pom.xml
+
+    - name: Build with Maven
+      shell: bash
+      run: |
+        mvn install
+
+# uncomment and set environment variables to push to Docker registry
+#    - name: Build and Push Docker Image
+#      shell: bash
+#      run: |
+#        mvn -pl webapp -Ddocker jib:build
+#      env:
+#        DOCKER_REGISTRY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME }}
+#        DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..568bbd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,45 @@
+*~
+*.swp
+*.class
+bin/
+target/
+target-ide/
+logs/
+.settings/
+.project
+.classpath
+.idea
+*.iml
+
+build/
+
+JArchitectOut/
+*.jdproj
+
+neo4j_DB/
+
+# log files
+datanucleus.log
+isis.log
+i18n-po.log
+hs_err_pid*.log
+
+# Package Files #
+*.war
+*.ear
+
+dependency-reduced-pom.xml
+pom.xml.tag
+pom.xml.next
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+
+.clover/
+
+
+rebel.xml
+translations.pot
+
+.flattened-pom.xml
+
+create-db-schema.sql
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e6e77b0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..47808f9
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,244 @@
+= SimpleApp
+:toc:
+:toc-placement!:
+
+image:https://github.com/apache/isis-app-simpleapp/workflows/Build%20w/%20Maven%20+%20Jdk%208/badge.svg[]
+image:https://github.com/apache/isis-app-simpleapp/workflows/Build%20w/%20Maven%20+%20Jdk%2015/badge.svg[]
+
+This is a simple link:http://isis.apache.org[Apache Isis] application, structured so it can be used as a starting point for developing your own applications.
+
+[TIP]
+====
+If all you want is get a feel for what the framework is all about, then take a look at the link:https://github.com/apache/isis-app-helloworld[HelloWorld] starter app, which is even simpler.
+====
+
+toc::[]
+
+== Quick start
+
+* install prereqs:
+
+** Java 8 LTS (eg link:https://adoptopenjdk.net/[Adopt OpenJDK] distribution)
+** Maven 3.6 or later (http://maven.apache.org/download.cgi[download])
+* download and unzip
++
+[source,bash]
+----
+APP=simpleapp
+BRANCH=master
+
+REPO=isis-app-$APP
+curl "https://codeload.github.com/apache/$REPO/zip/$BRANCH" | jar xv
+mv $REPO-$BRANCH $REPO
+cd $REPO
+----
+
+* Build using Maven:
++
+[source,bash]
+----
+mvn clean install
+----
+
+* Run using Maven:
++
+[source,bash]
+----
+mvn -pl webapp spring-boot:run
+----
+
+* Browse to http://localhost:8080.
+
+* Login to using:
+
+** username: `sven`
+** password: `pass`
+
++
+The app runs with H2 running in-memory, with sample data set up using fixture scripts.
+
+* Build a Docker image
++
+[source,bash]
+----
+export REVISION=...                 #<.>
+export DOCKER_REGISTRY_USERNAME     #<.>
+export DOCKER_REGISTRY_PASSWORD     #<.>
+
+mvn -pl webapp jib:build
+----
+<.> used as the image tag
+<.> Docker Hub registry username
+<.> Docker Hub registry password
++
+To push to another container registry, change the `<image>` tag in the pom.xml
+
+== Application Structure
+
+The following table explains the contents of each of the directories:
+
+[width="100%",options="header,footer",stripes="none",cols="2a,4a"]
+|====================
+|Directory
+|Description
+
+|`module-simple`
+|Holds the "simple" module, consisting of the `SimpleObject` entity and supporting services.
+It also contains module-specific unit- and integration tests.
+
+[TIP]
+====
+Larger applications should consist of multiple modules; each such module can be copied from this starter module.
+====
+
+|`webapp`
+|Holds the bootstrapping classes, along with application-level scoped services and home page.
+It also contains application-wide integration tests/BDD specs, and lockdown tests.
+
+The `pom.xml` also provides goals to run the app from the command line, or to be assembled into a Docker image.
+
+|====================
+
+
+== Development
+
+Apache Isis uses DataNucleus as its ORM, which requires that any entities are "enhanced", a post-compile process.
+
+Normally this is done as part of a "mvn clean install", but the entities can also be enhanced explicity using:
+
+[source,bash]
+----
+mvn -pl module-simple datanucleus:enhance -o
+----
+
+This is useful to know if the application or integration test fails to bootstrap, complaining of "unenhanced entities".
+
+TIP: You can also use `enhance-all.sh`
+
+
+== Testing
+
+The application has three types of tests.
+Each of these generates its own set of tests, and each can be disabled through a system property.
+
+.Testing types
+[cols="5a,12a,6a,3a", options="header"]
+|===
+
+| Test type
+| Report
+| Phase
+| Skip using
+
+
+| Unit test
+| `target/surefire-unittest-reports`
+| `test`
+| `-DskipUTs`
+
+| Integ test
+| `target/surefire-integtest-reports`
+| `integration-test`
+| `-DskipITs`
+
+| BDD (Cucumber) specs
+| `target/surefire-integbddspecs-reports` and +
+`target/cucumber-html-reports/overview-features.html`
+
+| `integration-test`
+| `-DskipBS`
+
+
+|===
+
+
+These outputs can for example be processed within/published by a continuous pipeline pipeline.
+
+
+=== Lockdown Tests
+
+Lockdown tests capture the state of the system, so that changes to that system can be captured and confirmed.
+There are two lockdown tests:
+
+* the metamodel (serialized as XML)
++
+This is expected to change whenever the domain logic is changed, but generally should _not_ change when the Apache Isis framework is upgraded.
+
+* Maven dependencies (serialized as flat files)
++
+This should only change when dependencies are bumped or new dependencies added.
+
+The lockdown tests are implemented as link:https://approvaltests.com/[approval test]s, meaning that the current state is compared to an "approved" state.
+If there are no differences then the test passes.
+If there are differences then the test fails, and a diff is shown using any available `diff` tool.
+
+The lockdown tests are disabled by default, and are enabled with system properties.
+
+* to compare the current metamodel with the previously approved metamodel:
++
+[source,bash]
+----
+mvn integration-test -Dmetamodel.lockdown
+----
+
+* to compare the current Maven dependencies with the previously approved dependencies:
++
+[source,bash]
+----
+mvn test -Dmavendeps.lockdown
+----
+
+When there are differences, the current state is written to the `current` subdirectory.
+If the current state is approved, it should be copied over to the corresponding `approved` subdirectory.
+
+
+
+
+== Translations
+
+Apache Isis supports i18n using link:https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html[GNU .po file]s.
+The `WEB-INF/translations.po` is the fallback (an empty value means that the key is used "as-is"), while `WEB-INF/translations-XX.po` files provide translations for each "XX" locale.
+
+Translations are required for all domain classes and all members (actions, properties and collections) of all classes.
+This information is available from the metamodel, and so a new template `translations.po` is generated as a side-effect of running the integration tests (through a log4j2 logger).
+A good integration test to run is `ValidateDomainModel_IntegTest`.
+
+In addition, translations are required for any validation messages triggered by the test.
+Running an integration tests that trigger validations will result in these messages being captured as keys, for example `Smoke_IntegTest`.
+
+The generated file should be merged with any existing translations in `WEB-INF/translations.po`, and translations obtained for any new keys (there are numerous online services that support the format).
+
+
+== Flyway
+
+The application also demonstrates how to use Flyway to migrate the database schema.
+
+By default the app runs using an in-memory database.
+The Flyway example is activated using the "SQLSERVER" Spring Boot profile, eg:
+
+[source,bash]
+----
+mvn -Dspring.profiles.active=SQLSERVER -pl webapp install
+mvn -Dspring.profiles.active=SQLSERVER -pl webapp spring-boot:run
+----
+
+This causes the properties defined in  `config/application-SQLSERVER.properties` file to be used in preference to those in the default `config/application.properties` file.
+It defines the following:
+
+* `spring.flyway.url`, `spring.flyway.user` and `spring.flyway.password`
++
+The presence of these is enough to enable the Flyway integration
+
+* `spring.flyway.enabled`
++
+This is explicitly set to `true`, to override the value in the default `config/application.properties`.
+
+* `isis.persistence.jdo-datanucleus.impl.datanucleus.schema.autoCreateAll`
++
+This is set to `false`, also overriding the value in the default `config/application.properties`.
+It instructs the JDO/DataNucleus object store not to automatically create any tables etc.
+
+The Spring Boot profile is also used to add the dependency to the SQL Server driver is included (it is hidden behind a Maven profile).
+
+The prerequisites to try this out are a SQL Server database running on `localhost` and with the credentials as specified in `config/application-SQLSERVER.properties`; adjust as necessary.
+
diff --git a/lib/README.txt b/lib/README.txt
new file mode 100644
index 0000000..db3c441
--- /dev/null
+++ b/lib/README.txt
@@ -0,0 +1,3 @@
+If eclipselink.weaving=true, then run using JVM argument:
+
+ -javaagent:lib/spring-instrument-5.3.8.jar
diff --git a/lib/spring-instrument-5.3.8.jar b/lib/spring-instrument-5.3.8.jar
new file mode 100644
index 0000000..c0a0a9b
Binary files /dev/null and b/lib/spring-instrument-5.3.8.jar differ
diff --git a/module-simple-tests/.gitignore b/module-simple-tests/.gitignore
new file mode 100644
index 0000000..85cb54f
--- /dev/null
+++ b/module-simple-tests/.gitignore
@@ -0,0 +1,8 @@
+.gradle
+translations.pot
+*.jar
+gradle/wrapper
+!gradle-wrapper.jar
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/module-simple-tests/log4j2-test.xml b/module-simple-tests/log4j2-test.xml
new file mode 100644
index 0000000..1d50129
--- /dev/null
+++ b/module-simple-tests/log4j2-test.xml
@@ -0,0 +1,60 @@
+<?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="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <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/module-simple-tests/pom.xml b/module-simple-tests/pom.xml
new file mode 100644
index 0000000..93a3504
--- /dev/null
+++ b/module-simple-tests/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-module-simple-tests</artifactId>
+    <name>SimpleApp (JPA) - Simple Module (tests)</name>
+
+    <description>
+        Integ tests for 'simple module'
+    </description>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+            <testResource>
+                <filtering>false</filtering>
+                <directory>src/test/java</directory>
+                <includes>
+                    <include>**</include>
+                </includes>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-module-simple</artifactId>
+        </dependency>
+
+        <!-- TESTS -->
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-unittests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+            	<exclusion>
+            		<groupId>org.jmock</groupId>
+            		<artifactId>jmock-junit4</artifactId>
+            	</exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-integtests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java
new file mode 100644
index 0000000..922b077
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObject_Test.java
@@ -0,0 +1,71 @@
+package domainapp.modules.simple.dom.so;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.title.TitleService;
+
+@ExtendWith(MockitoExtension.class)
+class SimpleObject_Test {
+
+    @Mock TitleService mockTitleService;
+    @Mock MessageService mockMessageService;
+    @Mock RepositoryService mockRepositoryService;
+
+    SimpleObject object;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        object = SimpleObject.withName("Foo");
+        object.titleService = mockTitleService;
+        object.messageService = mockMessageService;
+        object.repositoryService = mockRepositoryService;
+    }
+
+    @Nested
+    public class updateName {
+
+        @Test
+        void happy_case() {
+            // given
+            assertThat(object.getName()).isEqualTo("Foo");
+
+            // when
+            object.updateName("Bar");
+
+            // then
+            assertThat(object.getName()).isEqualTo("Bar");
+        }
+
+    }
+    @Nested
+    class delete {
+
+        @Test
+        void happy_case() throws Exception {
+
+            // given
+            assertThat(object).isNotNull();
+
+            // expecting
+            when(mockTitleService.titleOf(object)).thenReturn("Foo");
+
+            // when
+            object.delete();
+
+            // then
+            verify(mockMessageService).informUser("'Foo' deleted");
+            verify(mockRepositoryService).removeAndFlush(object);
+        }
+    }
+}
\ No newline at end of file
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java
new file mode 100644
index 0000000..88eaeb0
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/dom/so/SimpleObjects_Test.java
@@ -0,0 +1,80 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.stubbing.Answer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.when;
+
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.persistence.jpa.applib.services.JpaSupportService;
+
+@ExtendWith(MockitoExtension.class)
+class SimpleObjects_Test {
+
+    @Mock RepositoryService mockRepositoryService;
+    @Mock JpaSupportService mockJpaSupportService;
+    @Mock SimpleObjectRepository mockSimpleObjectRepository;
+
+    SimpleObjects objects;
+
+    @BeforeEach
+    public void setUp() {
+        objects = new SimpleObjects(mockRepositoryService, mockJpaSupportService, mockSimpleObjectRepository);
+    }
+
+    @Nested
+    class create {
+
+        @Test
+        void happyCase() {
+
+            // given
+            final String someName = "Foobar";
+
+            // expect
+            when(mockRepositoryService.persist(
+                    argThat((ArgumentMatcher<SimpleObject>) simpleObject -> Objects.equals(simpleObject.getName(), someName)))
+            ).then((Answer<SimpleObject>) invocation -> invocation.getArgument(0));
+
+            // when
+            final SimpleObject obj = objects.create(someName);
+
+            // then
+            assertThat(obj).isNotNull();
+            assertThat(obj.getName()).isEqualTo(someName);
+        }
+    }
+
+    @Nested
+    class ListAll {
+
+        @Test
+        void happyCase() {
+
+            // given
+            final List<SimpleObject> all = new ArrayList<>();
+
+            // expecting
+            when(mockSimpleObjectRepository.findAll())
+                .thenReturn(all);
+
+            // when
+            final List<SimpleObject> list = objects.listAll();
+
+            // then
+            assertThat(list).isEqualTo(all);
+        }
+    }
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java
new file mode 100644
index 0000000..a2b6476
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/SimpleModuleIntegTestAbstract.java
@@ -0,0 +1,50 @@
+package domainapp.modules.simple.integtests;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.test.context.ActiveProfiles;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
+import org.apache.isis.testing.fixtures.applib.IsisIntegrationTestAbstractWithFixtures;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+
+import domainapp.modules.simple.SimpleModule;
+
+
+@SpringBootTest(
+        classes = SimpleModuleIntegTestAbstract.TestApp.class
+)
+@ActiveProfiles("test")
+public abstract class SimpleModuleIntegTestAbstract extends IsisIntegrationTestAbstractWithFixtures {
+
+    /**
+     * Compared to the production app manifest <code>domainapp.webapp.AppManifest</code>,
+     * here we in effect disable security checks, and we exclude any web/UI modules.
+     */
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @Import({
+
+            IsisModuleCoreRuntimeServices.class,
+            IsisModuleSecurityBypass.class,
+            IsisModulePersistenceJpaEclipselink.class,
+            IsisModuleTestingFixturesApplib.class,
+
+            SimpleModule.class
+    })
+    @PropertySources({
+            @PropertySource(IsisPresets.H2InMemory_withUniqueSchema),
+            @PropertySource(IsisPresets.UseLog4j2Test),
+    })
+    public static class TestApp {
+
+    }
+
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java
new file mode 100644
index 0000000..761bb86
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObject_IntegTest.java
@@ -0,0 +1,85 @@
+package domainapp.modules.simple.integtests.tests;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.isis.applib.services.wrapper.DisabledException;
+import org.apache.isis.applib.services.wrapper.InvalidException;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+import domainapp.modules.simple.integtests.SimpleModuleIntegTestAbstract;
+
+@Transactional
+public class SimpleObject_IntegTest extends SimpleModuleIntegTestAbstract {
+
+    SimpleObject simpleObject;
+
+    @BeforeEach
+    public void setUp() {
+        // given
+        simpleObject = fixtureScripts.runPersona(SimpleObject_persona.FOO);
+    }
+
+
+    @Nested
+    public static class name extends SimpleObject_IntegTest {
+
+        @Test
+        public void accessible() {
+            // when
+            final String name = wrap(simpleObject).getName();
+
+            // then
+            assertThat(name).isEqualTo(simpleObject.getName());
+        }
+
+        @Test
+        public void not_editable() {
+
+            // expect
+            assertThrows(DisabledException.class, ()->{
+
+                // when
+                wrap(simpleObject).setName("new name");
+            });
+        }
+
+    }
+
+    @Nested
+    public static class updateName extends SimpleObject_IntegTest {
+
+
+        @Test
+        public void can_be_updated_directly() {
+
+            // when
+            wrap(simpleObject).updateName("new name");
+            transactionService.flushTransaction();
+
+            // then
+            assertThat(wrap(simpleObject).getName()).isEqualTo("new name");
+        }
+
+        @Test
+        public void fails_validation() {
+
+            // expect
+            InvalidException cause = assertThrows(InvalidException.class, ()->{
+
+                // when
+                wrap(simpleObject).updateName("new name!");
+            });
+
+            // then
+            assertThat(cause.getMessage()).contains("Character '!' is not allowed");
+        }
+    }
+
+}
diff --git a/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java
new file mode 100644
index 0000000..fdd26c2
--- /dev/null
+++ b/module-simple-tests/src/test/java/domainapp/modules/simple/integtests/tests/SimpleObjects_IntegTest.java
@@ -0,0 +1,95 @@
+package domainapp.modules.simple.integtests.tests;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.transaction.annotation.Transactional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.isis.testing.unittestsupport.applib.matchers.ThrowableMatchers;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+import domainapp.modules.simple.integtests.SimpleModuleIntegTestAbstract;
+
+@Transactional
+public class SimpleObjects_IntegTest extends SimpleModuleIntegTestAbstract {
+
+    @Inject
+    SimpleObjects menu;
+
+    @Nested
+    public static class listAll extends SimpleObjects_IntegTest {
+
+        @Test
+        public void happyCase() {
+
+            // given
+            fixtureScripts.run(new SimpleObject_persona.PersistAll());
+            transactionService.flushTransaction();
+
+            // when
+            final List<SimpleObject> all = wrap(menu).listAll();
+
+            // then
+            assertThat(all).hasSize(SimpleObject_persona.values().length);
+        }
+
+        @Test
+        public void whenNone() {
+
+            // when
+            final List<SimpleObject> all = wrap(menu).listAll();
+
+            // then
+            assertThat(all).hasSize(0);
+        }
+    }
+
+    @Nested
+    public static class create extends SimpleObjects_IntegTest {
+
+        @Test
+        public void happyCase() {
+
+            wrap(menu).create("Faz");
+
+            // then
+            final List<SimpleObject> all = wrap(menu).listAll();
+            assertThat(all).hasSize(1);
+        }
+
+        @Test
+        public void whenAlreadyExists() {
+
+            // given
+            fixtureScripts.runPersona(SimpleObject_persona.FIZZ);
+            transactionService.flushTransaction();
+
+            // expect
+            Throwable cause = assertThrows(Throwable.class, ()->{
+
+                // when
+                wrap(menu).create("Fizz");
+                transactionService.flushTransaction();
+
+            });
+
+            // also expect
+            MatcherAssert.assertThat(cause,
+                    ThrowableMatchers.causedBy(DuplicateKeyException.class));
+
+        }
+
+    }
+
+
+}
diff --git a/module-simple-tests/src/test/resources/application-test.yml b/module-simple-tests/src/test/resources/application-test.yml
new file mode 100644
index 0000000..3a68cae
--- /dev/null
+++ b/module-simple-tests/src/test/resources/application-test.yml
@@ -0,0 +1,4 @@
+isis:
+  persistence:
+    schema:
+      auto-create-schemas: "simple"
diff --git a/module-simple/.gitignore b/module-simple/.gitignore
new file mode 100644
index 0000000..85cb54f
--- /dev/null
+++ b/module-simple/.gitignore
@@ -0,0 +1,8 @@
+.gradle
+translations.pot
+*.jar
+gradle/wrapper
+!gradle-wrapper.jar
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/module-simple/pom.xml b/module-simple/pom.xml
new file mode 100644
index 0000000..d295539
--- /dev/null
+++ b/module-simple/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-module-simple</artifactId>
+    <name>SimpleApp (JPA) - Simple Module</name>
+
+    <build>
+        <resources>
+            <resource>
+                <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>
+
+        <!-- ISIS API -->
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-applib</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.persistence</groupId>
+            <artifactId>isis-persistence-jpa-eclipselink</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.testing</groupId>
+            <artifactId>isis-testing-fixtures-applib</artifactId>
+        </dependency>
+
+        <!-- IDE support (optional) -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java b/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java
new file mode 100644
index 0000000..e547259
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/SimpleModule.java
@@ -0,0 +1,28 @@
+package domainapp.modules.simple;
+
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+
+@Configuration
+@ComponentScan
+@EnableJpaRepositories
+@EntityScan(basePackageClasses = {SimpleModule.class})
+public class SimpleModule implements ModuleWithFixtures {
+
+    @Override
+    public FixtureScript getTeardownFixture() {
+        return new FixtureScript() {
+            @Override
+            protected void execute(ExecutionContext executionContext) {
+                repositoryService.removeAll(SimpleObject.class);
+            }
+        };
+    }
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt
new file mode 100644
index 0000000..ebc8999
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject#others.columnOrder.txt
@@ -0,0 +1,2 @@
+name
+id
\ No newline at end of file
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt
new file mode 100644
index 0000000..dbf267a
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.columnOrder.txt
@@ -0,0 +1,2 @@
+name
+#version
\ No newline at end of file
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java
new file mode 100644
index 0000000..094b99b
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.java
@@ -0,0 +1,140 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.Comparator;
+
+import javax.inject.Inject;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.PromptStyle;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Publishing;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.title.TitleService;
+import org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener;
+
+import static org.apache.isis.applib.annotation.SemanticsOf.IDEMPOTENT;
+import static org.apache.isis.applib.annotation.SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.val;
+
+import domainapp.modules.simple.types.Name;
+import domainapp.modules.simple.types.Notes;
+
+
+@javax.persistence.Entity
+@javax.persistence.Table(
+    schema="simple",
+    uniqueConstraints = {
+        @javax.persistence.UniqueConstraint(name = "SimpleObject__name__UNQ", columnNames = {"NAME"})
+    }
+)
+@javax.persistence.NamedQueries({
+        @javax.persistence.NamedQuery(
+                name = SimpleObject.NAMED_QUERY__FIND_BY_NAME_LIKE,
+                query = "SELECT so " +
+                        "FROM SimpleObject so " +
+                        "WHERE so.name LIKE :name"
+        )
+})
+@javax.persistence.EntityListeners(IsisEntityListener.class)
+@DomainObject(logicalTypeName = "simple.SimpleObject", entityChangePublishing = Publishing.ENABLED)
+@DomainObjectLayout()
+@NoArgsConstructor(access = AccessLevel.PUBLIC)
+@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
+@ToString(onlyExplicitlyIncluded = true)
+public class SimpleObject implements Comparable<SimpleObject> {
+
+    static final String NAMED_QUERY__FIND_BY_NAME_LIKE = "SimpleObject.findByNameLike";
+
+    @javax.persistence.Id
+    @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.AUTO)
+    @javax.persistence.Column(name = "id", nullable = false)
+    private Long id;
+
+    @javax.persistence.Version
+    @javax.persistence.Column(name = "version", nullable = false)
+    @PropertyLayout(fieldSetId = "metadata", sequence = "999")
+    @Getter @Setter
+    private long version;
+
+    public static SimpleObject withName(String name) {
+        val simpleObject = new SimpleObject();
+        simpleObject.setName(name);
+        return simpleObject;
+    }
+
+    @Inject @javax.persistence.Transient RepositoryService repositoryService;
+    @Inject @javax.persistence.Transient TitleService titleService;
+    @Inject @javax.persistence.Transient MessageService messageService;
+
+
+
+    @Title
+    @Name
+    @javax.persistence.Column(length = Name.MAX_LEN, nullable = false)
+    @Getter @Setter @ToString.Include
+    @PropertyLayout(fieldSetId = "name", sequence = "1")
+    private String name;
+
+    @Notes
+    @javax.persistence.Column(length = Notes.MAX_LEN, nullable = true)
+    @Getter @Setter
+    @Property(commandPublishing = Publishing.ENABLED, executionPublishing = Publishing.ENABLED)
+    @PropertyLayout(fieldSetId = "name", sequence = "2")
+    private String notes;
+
+
+    @Action(semantics = IDEMPOTENT, commandPublishing = Publishing.ENABLED, executionPublishing = Publishing.ENABLED)
+    @ActionLayout(associateWith = "name", promptStyle = PromptStyle.INLINE)
+    public SimpleObject updateName(
+            @Name final String name) {
+        setName(name);
+        return this;
+    }
+    public String default0UpdateName() {
+        return getName();
+    }
+    public String validate0UpdateName(String newName) {
+        for (char prohibitedCharacter : "&%$!".toCharArray()) {
+            if( newName.contains(""+prohibitedCharacter)) {
+                return "Character '" + prohibitedCharacter + "' is not allowed.";
+            }
+        }
+        return null;
+    }
+
+
+    @Action(semantics = NON_IDEMPOTENT_ARE_YOU_SURE)
+    @ActionLayout(
+            associateWith = "name", position = ActionLayout.Position.PANEL,
+            describedAs = "Deletes this object from the persistent datastore")
+    public void delete() {
+        final String title = titleService.titleOf(this);
+        messageService.informUser(String.format("'%s' deleted", title));
+        repositoryService.removeAndFlush(this);
+    }
+
+
+
+    private final static Comparator<SimpleObject> comparator =
+            Comparator.comparing(SimpleObject::getName);
+
+    @Override
+    public int compareTo(final SimpleObject other) {
+        return comparator.compare(this, other);
+    }
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml
new file mode 100644
index 0000000..46bd0ee
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.layout.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<bs3:grid xsi:schemaLocation="http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/grid/bootstrap3 http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd" xmlns:c="http://isis.apache.org/applib/layout/component" xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+    <bs3:row>
+        <bs3:col span="12" unreferencedActions="true">
+            <c:domainObject bookmarking="AS_ROOT"/>
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="6">
+            <bs3:tabGroup>
+                <bs3:tab name="General">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet id="name"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+                <bs3:tab name="Metadata">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet name="Metadata" id="metadata"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+                <bs3:tab name="Other">
+                    <bs3:row>
+                        <bs3:col span="12">
+                            <c:fieldSet id="other" unreferencedProperties="true"/>
+                        </bs3:col>
+                    </bs3:row>
+                </bs3:tab>
+            </bs3:tabGroup>
+            <bs3:tabGroup>
+            </bs3:tabGroup>
+        </bs3:col>
+        <bs3:col span="6">
+            <bs3:tabGroup  unreferencedCollections="true">
+            </bs3:tabGroup>
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png
new file mode 100644
index 0000000..0bd6f57
Binary files /dev/null and b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObject.png differ
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java
new file mode 100644
index 0000000..77d34a1
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjectRepository.java
@@ -0,0 +1,13 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SimpleObjectRepository extends JpaRepository<SimpleObject, Long> {
+
+    List<SimpleObject> findByNameContaining(final String name);
+
+    SimpleObject findByName(final String name);
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java
new file mode 100644
index 0000000..218d00b
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/dom/so/SimpleObjects.java
@@ -0,0 +1,92 @@
+package domainapp.modules.simple.dom.so;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.persistence.TypedQuery;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.BookmarkPolicy;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.PromptStyle;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.query.Query;
+import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.persistence.jpa.applib.services.JpaSupportService;
+
+import domainapp.modules.simple.types.Name;
+
+@DomainService(
+        nature = NatureOfService.VIEW,
+        logicalTypeName = "simple.SimpleObjects"
+)
+@javax.annotation.Priority(PriorityPrecedence.EARLY)
+@lombok.RequiredArgsConstructor(onConstructor_ = {@Inject} )
+public class SimpleObjects {
+
+    final RepositoryService repositoryService;
+    final JpaSupportService jpaSupportService;
+    final SimpleObjectRepository simpleObjectRepository;
+
+
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    @ActionLayout(promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public SimpleObject create(
+            @Name final String name) {
+        return repositoryService.persist(SimpleObject.withName(name));
+    }
+
+
+    @Action(semantics = SemanticsOf.NON_IDEMPOTENT)
+    @ActionLayout(promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public List<SimpleObject> findByNameLike(
+            @Name final String name) {
+        return repositoryService.allMatches(
+                Query.named(SimpleObject.class, SimpleObject.NAMED_QUERY__FIND_BY_NAME_LIKE)
+                     .withParameter("name", "%" + name + "%"));
+    }
+
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT, promptStyle = PromptStyle.DIALOG_SIDEBAR)
+    public List<SimpleObject> findByName(
+            @Name final String name
+            ) {
+        return simpleObjectRepository.findByNameContaining(name);
+    }
+
+
+    @Programmatic
+    public SimpleObject findByNameExact(final String name) {
+        return simpleObjectRepository.findByName(name);
+    }
+
+
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT)
+    public List<SimpleObject> listAll() {
+        return simpleObjectRepository.findAll();
+    }
+
+
+
+
+    @Programmatic
+    public void ping() {
+        jpaSupportService.getEntityManager(SimpleObject.class)
+            .ifSuccess(entityManager -> {
+                final TypedQuery<SimpleObject> q = entityManager.createQuery(
+                        "SELECT p FROM SimpleObject p ORDER BY p.name",
+                        SimpleObject.class)
+                    .setMaxResults(1);
+                q.getResultList();
+            });
+    }
+
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java
new file mode 100644
index 0000000..80223be
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObjectBuilder.java
@@ -0,0 +1,31 @@
+package domainapp.modules.simple.fixture;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.personas.BuilderScriptWithResult;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+@Accessors(chain = true)
+public class SimpleObjectBuilder extends BuilderScriptWithResult<SimpleObject> {
+
+    @Getter @Setter
+    private String name;
+
+    @Override
+    protected SimpleObject buildResult(final ExecutionContext ec) {
+
+        checkParam("name", ec, String.class);
+
+        return wrap(simpleObjects).create(name);
+    }
+
+    // -- DEPENDENCIES
+
+    @Inject SimpleObjects simpleObjects;
+
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java
new file mode 100644
index 0000000..41a5aec
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/fixture/SimpleObject_persona.java
@@ -0,0 +1,47 @@
+package domainapp.modules.simple.fixture;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.fixtures.applib.personas.PersonaWithBuilderScript;
+import org.apache.isis.testing.fixtures.applib.personas.PersonaWithFinder;
+import org.apache.isis.testing.fixtures.applib.setup.PersonaEnumPersistAll;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+public enum SimpleObject_persona
+implements PersonaWithBuilderScript<SimpleObjectBuilder>, PersonaWithFinder<SimpleObject> {
+
+    FOO("Foo"),
+    BAR("Bar"),
+    BAZ("Baz"),
+    FRODO("Frodo"),
+    FROYO("Froyo"),
+    FIZZ("Fizz"),
+    BIP("Bip"),
+    BOP("Bop"),
+    BANG("Bang"),
+    BOO("Boo");
+
+    private final String name;
+
+    @Override
+    public SimpleObjectBuilder builder() {
+        return new SimpleObjectBuilder().setName(name);
+    }
+
+    @Override
+    public SimpleObject findUsing(final ServiceRegistry serviceRegistry) {
+        SimpleObjects simpleObjects = serviceRegistry.lookupService(SimpleObjects.class).orElse(null);
+        return simpleObjects.findByNameExact(name);
+    }
+
+    public static class PersistAll
+    extends PersonaEnumPersistAll<SimpleObject_persona, SimpleObject> {
+
+        public PersistAll() {
+            super(SimpleObject_persona.class);
+        }
+    }
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/types/Name.java b/module-simple/src/main/java/domainapp/modules/simple/types/Name.java
new file mode 100644
index 0000000..8c3955c
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/types/Name.java
@@ -0,0 +1,20 @@
+package domainapp.modules.simple.types;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+
+@Property(maxLength = Name.MAX_LEN)
+@Parameter(maxLength = Name.MAX_LEN)
+@ParameterLayout(named = "Name")
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Name {
+
+    int MAX_LEN = 40;
+}
diff --git a/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java b/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java
new file mode 100644
index 0000000..73525e5
--- /dev/null
+++ b/module-simple/src/main/java/domainapp/modules/simple/types/Notes.java
@@ -0,0 +1,25 @@
+package domainapp.modules.simple.types;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+
+@Property(editing = Editing.ENABLED, maxLength = Notes.MAX_LEN)
+@PropertyLayout(named = "Notes", multiLine = 10, hidden = Where.ALL_TABLES)
+@Parameter(maxLength = Notes.MAX_LEN)
+@ParameterLayout(named = "Notes", multiLine = 10)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Notes {
+
+    int MAX_LEN = 4000;
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..f35ab68
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.app</groupId>
+        <artifactId>isis-app-starter-parent</artifactId>
+        <version>2.0.0-M6</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.apache.isis.starters</groupId>
+    <artifactId>simpleapp-jpa</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <name>SimpleApp (JPA) - Parent</name>
+
+    <packaging>pom</packaging>
+
+    <properties>
+        <isis.version>2.0.0-M6</isis.version>
+        <java.version>11</java.version>
+        <maven-cucumber-reporting.version>5.3.0</maven-cucumber-reporting.version>
+        <archunit.version>0.14.1</archunit.version>
+        <skipBDD>${skipTests}</skipBDD>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+
+            <!-- this project's own modules -->
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-module-simple</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-module-simple-tests</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-webapp</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>simpleapp-jpa-webapp-tests</artifactId>
+                <version>${project.version}</version>
+                <scope>test</scope>
+                <type>test-jar</type>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <modules>
+        <module>module-simple</module>
+        <module>module-simple-tests</module>
+        <module>webapp</module>
+        <module>webapp-tests</module>
+    </modules>
+
+    <profiles>
+        <profile>
+            <id>staging</id>
+            <activation>
+                <property>
+                    <name>!skip.staging</name>
+                </property>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>staging</id>
+                    <url>https://repository.apache.org/content/groups/staging/</url>
+                </repository>
+            </repositories>
+        </profile>
+        <profile>
+            <id>nightly</id>
+            <activation>
+                <property>
+                    <name>!skip.nightly</name>
+                </property>
+            </activation>
+            <repositories>
+                <repository>
+                    <id>nightly-builds</id>
+                    <url>https://nexus.incode.work/repository/nightly-builds/</url>
+                </repository>
+            </repositories>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/update-parent.sh b/update-parent.sh
new file mode 100644
index 0000000..de78ca7
--- /dev/null
+++ b/update-parent.sh
@@ -0,0 +1,15 @@
+
+if [ $# = 1 ]; then
+  PARENT=$1
+else
+  PARENT=$(curl -X GET "https://nexus.incode.work/service/rest/v1/search?sort=version&repository=nightly-builds&group=org.apache.isis.app&name=isis-app-starter-parent" -H "accept: application/json" -s | jq '.items[0].version' | sed 's/"//g')
+fi
+
+echo "parentVersion = $PARENT"
+mvn versions:update-parent -DparentVersion="[$PARENT]"
+
+CURR=$(grep "<isis.version>" pom.xml | head -1 | cut -d'>' -f2 | cut -d'<' -f1)
+sed -i "s|<isis.version>$CURR</isis.version>|<isis.version>$PARENT</isis.version>|g" pom.xml
+
+git add pom.xml
+git commit -m "updates parent pom to $PARENT"
diff --git a/webapp-tests/log4j2-test.xml b/webapp-tests/log4j2-test.xml
new file mode 100644
index 0000000..60e8974
--- /dev/null
+++ b/webapp-tests/log4j2-test.xml
@@ -0,0 +1,70 @@
+<?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>
+        <File name="TranslationsPoFile" fileName="translations.po" append="false" immediateFlush="true">
+            <PatternLayout>
+                <Pattern>%m%n</Pattern>
+            </PatternLayout>
+        </File>
+    </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="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+
+        <logger name="org.apache.isis.core.runtimeservices.i18n.po.PoWriter" level="info">
+            <AppenderRef ref="TranslationsPoFile"/>
+        </logger>
+
+    </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/webapp-tests/pom.xml b/webapp-tests/pom.xml
new file mode 100644
index 0000000..6110fb2
--- /dev/null
+++ b/webapp-tests/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-webapp-tests</artifactId>
+    <name>SimpleApp (JPA) - Webapp (tests)</name>
+
+    <description>
+        Integ tests for entire app.
+    </description>
+
+    <packaging>jar</packaging>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+            <testResource>
+                <filtering>false</filtering>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-webapp</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- TESTING -->
+
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-integtests</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+</project>
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java
new file mode 100644
index 0000000..5859b65
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/WebAppIntegTestAbstract.java
@@ -0,0 +1,55 @@
+package domainapp.webapp.integtests;
+
+import org.springframework.boot.SpringBootConfiguration;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.test.context.ActiveProfiles;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+import org.apache.isis.testing.integtestsupport.applib.IsisIntegrationTestAbstract;
+
+import domainapp.modules.simple.SimpleModule;
+import domainapp.webapp.application.ApplicationModule;
+
+@SpringBootTest(
+    classes = {
+            // we use a slightly different configuration compared to the production (AppManifest/webapp)
+            WebAppIntegTestAbstract.TestApp.class,
+            ApplicationModule.class,
+    }
+)
+@ActiveProfiles("test")
+public abstract class WebAppIntegTestAbstract extends IsisIntegrationTestAbstract {
+
+    /**
+     * Compared to the production app manifest <code>domainapp.webapp.AppManifest</code>,
+     * here we in effect disable security checks, and we exclude any web/UI modules.
+     */
+    @SpringBootConfiguration
+    @EnableAutoConfiguration
+    @EnableJpaRepositories
+    @Import({
+
+        IsisModuleCoreRuntimeServices.class,
+        IsisModuleSecurityBypass.class,
+        IsisModulePersistenceJpaEclipselink.class,
+        IsisModuleTestingFixturesApplib.class,
+
+        SimpleModule.class
+    })
+    @PropertySources({
+        @PropertySource(IsisPresets.H2InMemory_withUniqueSchema),
+        @PropertySource(IsisPresets.UseLog4j2Test),
+    })
+    public static class TestApp {
+
+    }
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java
new file mode 100644
index 0000000..d2b345d
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/SwaggerExport_IntegTest.java
@@ -0,0 +1,34 @@
+package domainapp.webapp.integtests.metamodel;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.annotation.DirtiesContext;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.applib.services.swagger.Format;
+import org.apache.isis.applib.services.swagger.Visibility;
+import org.apache.isis.testing.integtestsupport.applib.swagger.SwaggerExporter;
+import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
+
+import lombok.val;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+
+@Import({
+        IsisModuleViewerRestfulObjectsJaxrsResteasy4.class
+})
+@DirtiesContext
+class SwaggerExport_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject ServiceRegistry serviceRegistry;
+
+    @Test
+    void export() throws IOException {
+        val swaggerExporter = new SwaggerExporter(serviceRegistry);
+        swaggerExporter.export(Visibility.PRIVATE, Format.JSON);
+    }
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java
new file mode 100644
index 0000000..84b9b0c
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/metamodel/ValidateDomainModel_IntegTest.java
@@ -0,0 +1,22 @@
+package domainapp.webapp.integtests.metamodel;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.apache.isis.testing.integtestsupport.applib.validate.DomainModelValidator;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+
+class ValidateDomainModel_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject ServiceRegistry serviceRegistry;
+
+    @Test
+    void validate() {
+        new DomainModelValidator(serviceRegistry).assertValid();
+    }
+
+
+}
diff --git a/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java b/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java
new file mode 100644
index 0000000..f8c5b56
--- /dev/null
+++ b/webapp-tests/src/test/java/domainapp/webapp/integtests/smoke/Smoke_IntegTest.java
@@ -0,0 +1,92 @@
+package domainapp.webapp.integtests.smoke;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.services.wrapper.InvalidException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.transaction.annotation.Transactional;
+
+import org.apache.isis.applib.services.xactn.TransactionService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import domainapp.webapp.integtests.WebAppIntegTestAbstract;
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Transactional
+class Smoke_IntegTest extends WebAppIntegTestAbstract {
+
+    @Inject SimpleObjects menu;
+    @Inject TransactionService transactionService;
+
+    @Test
+    void happy_case() {
+
+        // when
+        List<SimpleObject> all = wrap(menu).listAll();
+
+        // then
+        assertThat(all).isEmpty();
+
+
+        // when
+        final SimpleObject fred = wrap(menu).create("Fred");
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(1);
+        assertThat(all).contains(fred);
+
+
+        // when
+        final SimpleObject bill = wrap(menu).create("Bill");
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(2);
+        assertThat(all).contains(fred, bill);
+
+
+        // when
+        wrap(fred).updateName("Freddy");
+        transactionService.flushTransaction();
+
+        // then
+        assertThat(wrap(fred).getName()).isEqualTo("Freddy");
+
+
+        // when
+        wrap(fred).setNotes("These are some notes");
+        transactionService.flushTransaction();
+
+        // then
+        assertThat(wrap(fred).getNotes()).isEqualTo("These are some notes");
+
+
+        // when
+        Assertions.assertThrows(InvalidException.class, () -> {
+            wrap(fred).updateName("New name !!!");
+            transactionService.flushTransaction();
+        }, "Exclamation mark is not allowed");
+
+        // then
+        assertThat(wrap(fred).getNotes()).isEqualTo("These are some notes");
+
+
+        // when
+        wrap(fred).delete();
+        transactionService.flushTransaction();
+
+        // then
+        all = wrap(menu).listAll();
+        assertThat(all).hasSize(1);
+    }
+
+}
+
diff --git a/webapp-tests/src/test/resources/application-test.yml b/webapp-tests/src/test/resources/application-test.yml
new file mode 100644
index 0000000..3a68cae
--- /dev/null
+++ b/webapp-tests/src/test/resources/application-test.yml
@@ -0,0 +1,4 @@
+isis:
+  persistence:
+    schema:
+      auto-create-schemas: "simple"
diff --git a/webapp-tests/src/test/resources/junit-platform.properties b/webapp-tests/src/test/resources/junit-platform.properties
new file mode 100644
index 0000000..182483e
--- /dev/null
+++ b/webapp-tests/src/test/resources/junit-platform.properties
@@ -0,0 +1,17 @@
+# as per https://github.com/cucumber/cucumber-jvm/tree/master/junit-platform-engine#configuration-options
+cucumber.publish.quiet=true
+cucumber.filter.tags=not @backlog and not @ignore
+#we are using @Cucumber annotated classes and restrict classpath search to
+cucumber.glue=domainapp.webapp.bdd
+#we are using built-in reporting plugins
+cucumber.plugin=pretty, html:target/cucumber-reports/cucumber-report.html
+
+# WARNING:
+#
+# cucumber.plugin=..., json:target/cucumber-reports/cucumber-report.json, ...
+#
+# will cause an empty file to be created when running from mvn.
+#
+# this is why the maven configuration to execute cucumber using the CLI (antrun:run@cucumber-cli)
+# is configured to use --plugins json:target/cucumber-no-clobber.json
+#
\ No newline at end of file
diff --git a/webapp/pom.xml b/webapp/pom.xml
new file mode 100644
index 0000000..edc9b65
--- /dev/null
+++ b/webapp/pom.xml
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.starters</groupId>
+        <artifactId>simpleapp-jpa</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>simpleapp-jpa-webapp</artifactId>
+    <name>SimpleApp (JPA) - Webapp</name>
+
+    <description>
+    	Assembles and runs both the Wicket viewer and the Restfulobjects viewer
+    	in a single webapp configured to run using the JPA/EclipseLink object store.
+    </description>
+
+    <packaging>jar</packaging>
+
+    <build>
+        <resources>
+            <resource>
+                <filtering>true</filtering>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>application.yml</include>
+                </includes>
+            </resource>
+            <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>
+        <plugins>
+            <!-- running: mvn spring-boot:run -->
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <classifier>exec</classifier>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>simpleapp-jpa-module-simple</artifactId>
+        </dependency>
+
+        <!-- isis -->
+        <dependency>
+            <groupId>org.apache.isis.mavendeps</groupId>
+            <artifactId>isis-mavendeps-webapp</artifactId>
+            <type>pom</type>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.security</groupId>
+            <artifactId>isis-security-shiro</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.persistence</groupId>
+            <artifactId>isis-persistence-jpa-eclipselink</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-actuator-autoconfigure</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.testing</groupId>
+            <artifactId>isis-testing-h2console-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.extensions</groupId>
+            <artifactId>isis-extensions-flyway-impl</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-quartz</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+
+        <profile>
+            <id>jdbc-sqlserver</id>
+            <activation>
+                <property>
+                    <name>spring.profiles.active</name>
+                    <value>SQLSERVER</value>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>com.microsoft.sqlserver</groupId>
+                    <artifactId>mssql-jdbc</artifactId>
+                    <version>7.4.1.jre8</version>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>jdbc-postgresql</id>
+            <activation>
+                <property>
+                    <name>spring.profiles.active</name>
+                    <value>POSTGRESQL</value>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.postgresql</groupId>
+		    		<artifactId>postgresql</artifactId>
+					<!-- <version>42.2.12</version> ... version already managed -->
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>deploy-to-docker-io</id>
+            <activation>
+                <property>
+                    <name>docker</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>com.google.cloud.tools</groupId>
+                        <artifactId>jib-maven-plugin</artifactId>
+                        <configuration>
+                            <from>
+                                <image>adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.10_9</image>
+                            </from>
+                            <container>
+                                <jvmFlags>
+                                    <jvmFlag>-Xmx512m</jvmFlag>
+                                </jvmFlags>
+                                <mainClass>domainapp.webapp.SimpleApp</mainClass>
+                                <ports>
+                                    <port>8080</port>
+                                </ports>
+                            </container>
+                            <to>
+                                <image>docker.io/apacheisis/simpleapp</image>
+                                <tags>
+                                    <tag>${env.REVISION}</tag>
+                                </tags>
+                                <auth>
+                                    <username>${env.DOCKER_REGISTRY_USERNAME}</username>
+                                    <password>${env.DOCKER_REGISTRY_PASSWORD}</password>
+                                </auth>
+                            </to>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git a/webapp/src/main/java/domainapp/webapp/AppManifest.java b/webapp/src/main/java/domainapp/webapp/AppManifest.java
new file mode 100644
index 0000000..9dca47c
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/AppManifest.java
@@ -0,0 +1,47 @@
+package domainapp.webapp;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.context.annotation.PropertySources;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.extensions.flyway.impl.IsisModuleExtFlywayImpl;
+import org.apache.isis.persistence.jpa.eclipselink.IsisModulePersistenceJpaEclipselink;
+import org.apache.isis.security.shiro.IsisModuleSecurityShiro;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
+import org.apache.isis.testing.h2console.ui.IsisModuleTestingH2ConsoleUi;
+import org.apache.isis.viewer.restfulobjects.jaxrsresteasy4.IsisModuleViewerRestfulObjectsJaxrsResteasy4;
+import org.apache.isis.viewer.wicket.viewer.IsisModuleViewerWicketViewer;
+
+import domainapp.webapp.application.ApplicationModule;
+import domainapp.webapp.application.fixture.scenarios.DomainAppDemo;
+import domainapp.webapp.custom.CustomModule;
+import domainapp.webapp.quartz.QuartzModule;
+
+@Configuration
+@Import({
+        IsisModuleCoreRuntimeServices.class,
+        IsisModuleSecurityShiro.class,
+        IsisModulePersistenceJpaEclipselink.class,
+        IsisModuleViewerRestfulObjectsJaxrsResteasy4.class,
+        IsisModuleViewerWicketViewer.class,
+
+        IsisModuleTestingFixturesApplib.class,
+        IsisModuleTestingH2ConsoleUi.class,
+
+        IsisModuleExtFlywayImpl.class,
+
+        ApplicationModule.class,
+        CustomModule.class,
+        QuartzModule.class,
+
+        // discoverable fixtures
+        DomainAppDemo.class
+})
+@PropertySources({
+        @PropertySource(IsisPresets.DebugDiscovery),
+})
+public class AppManifest {
+}
diff --git a/webapp/src/main/java/domainapp/webapp/SimpleApp.java b/webapp/src/main/java/domainapp/webapp/SimpleApp.java
new file mode 100644
index 0000000..7dedb13
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/SimpleApp.java
@@ -0,0 +1,26 @@
+package domainapp.webapp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Import;
+
+import org.apache.isis.core.config.presets.IsisPresets;
+
+@SpringBootApplication
+@Import({
+    AppManifest.class
+//    , XrayEnable.class
+})
+public class SimpleApp extends SpringBootServletInitializer {
+
+    /**
+     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an
+     * entry point by searching for classes having a {@code main(...)}
+     */
+    public static void main(String[] args) {
+        IsisPresets.prototyping();
+        SpringApplication.run(new Class[] { SimpleApp.class }, args);
+    }
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java b/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java
new file mode 100644
index 0000000..540c2e4
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/ApplicationModule.java
@@ -0,0 +1,14 @@
+package domainapp.webapp.application;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+import domainapp.modules.simple.SimpleModule;
+
+@Configuration
+@Import(SimpleModule.class)
+@ComponentScan
+public class ApplicationModule {
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java b/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java
new file mode 100644
index 0000000..3b18a98
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/fixture/scenarios/DomainAppDemo.java
@@ -0,0 +1,20 @@
+package domainapp.webapp.application.fixture.scenarios;
+
+import javax.inject.Inject;
+
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixturesService;
+
+import domainapp.modules.simple.fixture.SimpleObject_persona;
+
+public class DomainAppDemo extends FixtureScript {
+
+    @Override
+    protected void execute(final ExecutionContext ec) {
+        ec.executeChildren(this, moduleWithFixturesService.getTeardownFixture());
+        ec.executeChild(this, new SimpleObject_persona.PersistAll());
+    }
+
+    @Inject ModuleWithFixturesService moduleWithFixturesService;
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java b/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java
new file mode 100644
index 0000000..4f16352
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/health/HealthCheckServiceImpl.java
@@ -0,0 +1,33 @@
+package domainapp.webapp.application.services.health;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.services.health.Health;
+import org.apache.isis.applib.services.health.HealthCheckService;
+
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Service
+@Named("domainapp.HealthCheckServiceImpl")
+public class HealthCheckServiceImpl implements HealthCheckService {
+
+    private final SimpleObjects simpleObjects;
+
+    @Inject
+    public HealthCheckServiceImpl(SimpleObjects simpleObjects) {
+        this.simpleObjects = simpleObjects;
+    }
+
+    @Override
+    public Health check() {
+        try {
+            simpleObjects.ping();
+            return Health.ok();
+        } catch (Exception ex) {
+            return Health.error(ex);
+        }
+    }
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java
new file mode 100644
index 0000000..bfb4dca
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.java
@@ -0,0 +1,32 @@
+package domainapp.webapp.application.services.homepage;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.HomePage;
+import org.apache.isis.applib.annotation.Nature;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@DomainObject(
+        nature = Nature.VIEW_MODEL,
+        logicalTypeName = "simple.HomePageViewModel"
+        )
+@HomePage
+@DomainObjectLayout()
+public class HomePageViewModel {
+
+    public String title() {
+        return getObjects().size() + " objects";
+    }
+
+    public List<SimpleObject> getObjects() {
+        return simpleObjects.listAll();
+    }
+
+    @Inject SimpleObjects simpleObjects;
+}
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml
new file mode 100644
index 0000000..72ba56e
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.layout.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<bs3:grid xsi:schemaLocation="http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/grid/bootstrap3 http://isis.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd" xmlns="http://isis.apache.org/applib/layout/component" xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <bs3:row>
+        <bs3:col span="3">
+            <bs3:row>
+                <bs3:col span="12" unreferencedActions="true">
+                    <domainObject/>
+                    <action id="clearHints" hidden="EVERYWHERE"/>
+                    <action id="impersonate" hidden="EVERYWHERE"/>
+                    <action id="impersonateWithRoles" hidden="EVERYWHERE"/>
+                    <action id="stopImpersonating" hidden="EVERYWHERE"/>
+                    <action id="downloadLayoutXml" hidden="EVERYWHERE"/>
+                    <action id="inspectMetamodel" hidden="EVERYWHERE"/>
+                    <action id="rebuildMetamodel" hidden="EVERYWHERE"/>
+                    <action id="downloadMetamodelXml" hidden="EVERYWHERE"/>
+                    <action id="openRestApi" hidden="EVERYWHERE"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="6" unreferencedCollections="true">
+            <bs3:row>
+                <bs3:col span="12">
+                    <collection id="objects" defaultView="table"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="3">
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="0">
+            <fieldSet name="General" id="general" unreferencedProperties="true"/>
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png
new file mode 100644
index 0000000..cb03785
Binary files /dev/null and b/webapp/src/main/java/domainapp/webapp/application/services/homepage/HomePageViewModel.png differ
diff --git a/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java b/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java
new file mode 100644
index 0000000..9004096
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/custom/CustomModule.java
@@ -0,0 +1,10 @@
+package domainapp.webapp.custom;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan
+public class CustomModule {
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java b/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java
new file mode 100644
index 0000000..33c2ee5
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/custom/restapi/CustomController.java
@@ -0,0 +1,47 @@
+package domainapp.webapp.custom.restapi;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.services.xactn.TransactionalProcessor;
+
+import lombok.RequiredArgsConstructor;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@RestController
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+class CustomController {
+
+    private final InteractionService interactionService;
+    private final TransactionalProcessor transactionalProcessor;
+    private final SimpleObjects simpleObjects;
+
+    @GetMapping("/custom/simpleObjects")
+    List<SimpleObject> all() {
+        return call("sven", simpleObjects::listAll)
+                .orElse(Collections.<SimpleObject>emptyList());
+    }
+
+    private <T> Optional<T> call(
+            final String username,
+            final Callable<T> callable) {
+
+        return interactionService.call(
+                InteractionContext.ofUserWithSystemDefaults(UserMemento.ofName(username)),
+                () -> transactionalProcessor.callWithinCurrentTransactionElseCreateNew(callable))
+                .optionalElseFail(); // re-throws exception that has occurred, if any
+    }
+
+}
diff --git a/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java b/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java
new file mode 100644
index 0000000..3220d64
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/quartz/QuartzModule.java
@@ -0,0 +1,41 @@
+package domainapp.webapp.quartz;
+
+import org.quartz.JobDetail;
+import org.quartz.SimpleTrigger;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.quartz.JobDetailFactoryBean;
+import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
+
+import lombok.val;
+
+import domainapp.webapp.quartz.job.SampleJob;
+
+@Configuration
+@ComponentScan
+public class QuartzModule {
+
+    private static final int REPEAT_INTERVAL_SECS = 60;
+    private static final int START_DELAY_SECS = 20;
+    private static final int MILLIS_PER_SEC = 1000;
+
+    @Bean
+    public JobDetailFactoryBean jobDetail() {
+        val jobDetailFactory = new JobDetailFactoryBean();
+        jobDetailFactory.setJobClass(SampleJob.class);
+        jobDetailFactory.setDescription("Invoke Sample Job service...");
+        jobDetailFactory.setDurability(true);
+        return jobDetailFactory;
+    }
+
+    @Bean
+    public SimpleTriggerFactoryBean trigger(JobDetail job) {
+        val trigger = new SimpleTriggerFactoryBean();
+        trigger.setJobDetail(job);
+        trigger.setStartDelay(START_DELAY_SECS * MILLIS_PER_SEC);
+        trigger.setRepeatInterval(REPEAT_INTERVAL_SECS * MILLIS_PER_SEC);
+        trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
+        return trigger;
+    }
+}
diff --git a/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java b/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java
new file mode 100644
index 0000000..d112ae9
--- /dev/null
+++ b/webapp/src/main/java/domainapp/webapp/quartz/job/SampleJob.java
@@ -0,0 +1,55 @@
+package domainapp.webapp.quartz.job;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.stereotype.Component;
+
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
+import org.apache.isis.applib.services.iactnlayer.InteractionService;
+import org.apache.isis.applib.services.user.UserMemento;
+import org.apache.isis.applib.services.xactn.TransactionalProcessor;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+
+import domainapp.modules.simple.dom.so.SimpleObject;
+import domainapp.modules.simple.dom.so.SimpleObjects;
+
+@Component
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
+public class SampleJob implements Job {
+
+    private final InteractionService interactionService;
+    private final TransactionalProcessor transactionalProcessor;
+    private final SimpleObjects simpleObjects;
+
+    @Override
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        final List<SimpleObject> all = all();
+        log.info("{} objects in the database", all.size());
+    }
+
+    List<SimpleObject> all() {
+        return call("sven", simpleObjects::listAll)
+                .orElse(Collections.<SimpleObject>emptyList());
+    }
+
+    private <T> Optional<T> call(
+            final String username,
+            final Callable<T> callable) {
+
+        return interactionService.call(
+                InteractionContext.ofUserWithSystemDefaults(UserMemento.ofName(username)),
+                () -> transactionalProcessor.callWithinCurrentTransactionElseCreateNew(callable))
+                .optionalElseFail(); // re-throws exception that has occurred, if any
+    }
+}
diff --git a/webapp/src/main/resources/application.yml b/webapp/src/main/resources/application.yml
new file mode 100644
index 0000000..01efdda
--- /dev/null
+++ b/webapp/src/main/resources/application.yml
@@ -0,0 +1,106 @@
+#
+# Recommend use for static configuration that does not change between environments.
+#
+# See also config/application.properties
+#
+isis:
+
+  applib:
+    annotation:
+      action:
+        explicit: true
+        command-publishing: ignore_safe
+        execution-publishing: all
+      action-layout:
+        css-class:
+          patterns:
+            delete.*:btn-danger,
+            discard.*:btn-warning,
+            remove.*:btn-warning
+      property:
+        command-publishing: all
+        execution-publishing: all
+      domain-object:
+        editing: false
+        entity-change-publishing: all
+
+  core:
+    meta-model:
+      introspector:
+        lock-after-full-introspection: true
+      validator:
+        allow-deprecated: false
+        no-params-only: true
+        explicit-object-type: true
+
+    runtime-services:
+      translation:
+        po:
+          mode: disabled
+
+  viewer:
+    wicket:
+      application:
+        about: Simple App
+        brand-logo-header: images/apache-isis/logo-48x48.png
+        css: css/application.css
+        favicon-url: images/favicon.png
+        js: scripts/application.js
+        menubars-layout-xml: menubars.layout.xml
+        name: Simple App
+        # https://stackoverflow.com/a/38983935/56880
+        version: @project.version@
+
+      credit:
+        - url:  http://isis.apache.org
+          image: images/apache-isis/logo-48x48.png
+          name: Apache Isis
+
+      bookmarked-pages:
+        show-chooser: true
+        show-drop-down-on-footer: true
+
+      max-title-length-in-standalone-tables: 0
+      max-title-length-in-parented-tables: 0
+
+      themes:
+        show-chooser: true
+
+  testing:
+    fixtures:
+      fixture-scripts-specification:
+        context-class: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        run-script-default: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        recreate: domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+        non-persisted-objects-strategy: persist
+        multiple-execution-strategy: execute
+
+eclipselink:
+  # if enabled, then must be run with JVM arg:
+  # -javaagent:lib/spring-instrument-5.3.8.jar
+  weaving: false
+  # if weaving subproperties are required, then specify all eclipselink.weaving
+  # properties using application.properties instead (it's not possible to
+  # specify both eclipselink.weaving property and its subproperties using yaml syntax)
+
+resteasy:
+  jaxrs:
+    app:
+      registration: beans
+    defaultPath: "/restful"
+
+server:
+  max-http-header-size: 16KB
+
+spring:
+  banner:
+    location: banner.txt
+
+  quartz:
+    job-store-type: memory
+
+management:
+  endpoint:
+    health:
+      enabled: true
+
diff --git a/webapp/src/main/resources/banner.txt b/webapp/src/main/resources/banner.txt
new file mode 100644
index 0000000..1348a76
--- /dev/null
+++ b/webapp/src/main/resources/banner.txt
@@ -0,0 +1,6 @@
+     _                     _            ___     _
+    / \   _ __   __ _  ___| |__   ___  |_ _|___(_)___
+   / _ \ | '_ \ / _` |/ __| '_ \ / _ \  | |/ __| / __|
+  / ___ \| |_) | (_| | (__| | | |  __/  | |\__ \ \__ \
+ /_/   \_\ .__/ \__,_|\___|_| |_|\___| |___|___/_|___/
+         |_|
\ No newline at end of file
diff --git a/webapp/src/main/resources/config/application-SQLSERVER.properties b/webapp/src/main/resources/config/application-SQLSERVER.properties
new file mode 100644
index 0000000..07d8fe3
--- /dev/null
+++ b/webapp/src/main/resources/config/application-SQLSERVER.properties
@@ -0,0 +1,31 @@
+#
+# Recommend use for configuration that changes between environments.
+#
+# To override externally, see Spring Boot docs
+# https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
+#
+# See also /application.yml
+#
+
+#
+# to enable this profile use:
+#
+# -Dspring.profiles.active=SQLSERVER
+#
+# Prereq is an empty database called 'simpleapp', with user and password also 'simpleapp'
+#
+
+spring.flyway.enabled=true
+spring.flyway.default-schema=SIMPLE
+spring.flyway.schemas=SIMPLE
+
+spring.datasource.platform=sqlserver
+spring.datasource.url=jdbc:sqlserver://localhost;databaseName=simpleapp
+spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
+spring.datasource.username=simpleapp
+spring.datasource.password=simpleapp
+
+#isis.persistence.schema.create-schema-sql-template=   (use flyway instead)
+isis.persistence.schema.auto-create-schemas=
+
+
diff --git a/webapp/src/main/resources/config/application.properties b/webapp/src/main/resources/config/application.properties
new file mode 100644
index 0000000..8a7dcce
--- /dev/null
+++ b/webapp/src/main/resources/config/application.properties
@@ -0,0 +1,31 @@
+#
+# Recommend use for configuration that changes between environments.
+#
+# To override externally, see Spring Boot docs
+# https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
+#
+# See also /application.yml
+#
+
+
+spring.flyway.enabled=false
+
+spring.sql.init.platform=h2
+spring.datasource.url=jdbc:h2:mem:simple;DATABASE_TO_UPPER=false
+spring.datasource.driver-class-name=org.h2.Driver
+
+isis.persistence.schema.create-schema-sql-template=CREATE SCHEMA IF NOT EXISTS %s
+isis.persistence.schema.auto-create-schemas=simple
+
+#eclipselink.weaving=true
+eclipselink.deploy-on-startup=true
+eclipselink.ddl-generation.output-mode=both
+eclipselink.ddl-generation=create-tables
+eclipselink.create-ddl-jdbc-file-name=create-db-schema.sql
+eclipselink.application-location=.
+eclipselink.jpa.upper-case-column-names=false
+
+# uncomment to run during bootstrap
+#isis.testing.fixtures.initial-script = domainapp.webapp.application.fixture.scenarios.DomainAppDemo
+
+
diff --git a/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql
new file mode 100644
index 0000000..447a66f
--- /dev/null
+++ b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.10__create_schema_simple.sql
@@ -0,0 +1,8 @@
+IF NOT EXISTS (
+    SELECT  1
+    FROM    sys.schemas
+    WHERE   name = N'simple'
+)
+    EXEC('CREATE SCHEMA [simple]');
+GO
+
diff --git a/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql
new file mode 100644
index 0000000..0d727cf
--- /dev/null
+++ b/webapp/src/main/resources/db/migration/SQLSERVER/V2020.01.20.14.14__create_table_simple_SimpleObject.sql
@@ -0,0 +1,25 @@
+SET ANSI_NULLS ON
+GO
+
+SET QUOTED_IDENTIFIER ON
+GO
+
+CREATE TABLE [simple].[SimpleObject](
+    [id]      [bigint]        IDENTITY(1,1) NOT NULL,
+    [name]    [varchar](40)                 NOT NULL,
+    [notes]   [varchar](4000)               NULL,
+    [version] [datetime2](7)                NOT NULL,
+
+    CONSTRAINT [SimpleObject_PK] PRIMARY KEY CLUSTERED
+        ([id] ASC)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY],
+
+    CONSTRAINT [SimpleObject_name_UNQ] UNIQUE NONCLUSTERED
+        ([name] ASC)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY]
+)
+ON [PRIMARY]
+GO
+
diff --git a/webapp/src/main/resources/log4j2-spring.xml b/webapp/src/main/resources/log4j2-spring.xml
new file mode 100644
index 0000000..fa5582e
--- /dev/null
+++ b/webapp/src/main/resources/log4j2-spring.xml
@@ -0,0 +1,64 @@
+<?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>
+
+        <!-- silence Wicket -->
+        <Logger name="org.apache.wicket.page.PartialPageUpdate" level="error" />
+
+        <Logger name="org.apache.isis.applib.services.publishing.log.CommandLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.ExecutionLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.EntityChangesLogger" level="debug" />
+        <Logger name="org.apache.isis.applib.services.publishing.log.EntityPropertyChangeLogger" level="debug" />
+
+        <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="org.apache.directory" level="warn"/>
+        <logger name="org.apache.directory.api.ldap.model.entry.Value" level="off"/>
+
+        <logger name="DataNucleus.Persistence" level="info"/>
+        <logger name="DataNucleus.Transaction" level="info"/>
+        <logger name="DataNucleus.Datastore.Schema" level="info"/>
+        <logger name="DataNucleus.Datastore.Native" level="info"/>
+
+        <logger name="org.apache.isis.applib.services.publishing.log.CommandLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.EntityChangesLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.EntityPropertyChangeLogger" level="debug"/>
+        <logger name="org.apache.isis.applib.services.publishing.log.ExecutionLogger" level="debug"/>
+
+        <!-- request scoped -->
+        <logger name="org.apache.isis.viewer.restfulobjects.rendering.service.acceptheader.AcceptHeaderServiceForRest" level="debug"/>
+        <logger name="org.apache.isis.applib.services.command.CommandContext" level="debug"/>
+        <logger name="org.apache.isis.applib.services.iactn.InteractionProvider" level="debug"/>
+        <logger name="org.apache.isis.applib.services.scratchpad.Scratchpad" level="debug"/>
+        <logger name="org.apache.isis.core.runtimeservices.publish.PublisherDispatchServiceDefault" level="debug"/>
+
+        <!-- transaction scoped -->
+        <logger name="org.apache.isis.core.transaction.changetracking.EntityChangeTrackerDefault" level="debug"/>
+        <logger name="org.apache.isis.core.runtimeservices.publish.ExecutionPublisherDefault" level="debug"/>
+        <logger name="org.apache.isis.applib.services.queryresultscache.QueryResultsCache" level="debug"/>
+
+        <Root level="info">
+            <AppenderRef ref="Console" />
+        </Root>
+    </Loggers>
+</Configuration>
diff --git a/webapp/src/main/resources/menubars.layout.xml b/webapp/src/main/resources/menubars.layout.xml
new file mode 100644
index 0000000..b098222
--- /dev/null
+++ b/webapp/src/main/resources/menubars.layout.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<mb3:menuBars xsi:schemaLocation="http://isis.apache.org/applib/layout/menubars/bootstrap3 http://isis.apache.org/applib/layout/menubars/bootstrap3/menubars.xsd http://isis.apache.org/applib/layout/component http://isis.apache.org/applib/layout/component/component.xsd http://isis.apache.org/applib/layout/links http://isis.apache.org/applib/layout/links/links.xsd" xmlns:cpt="http://isis.apache.org/applib/layout/component" xmlns:lnk="http://isis.apache.org/applib/layout/links" xmlns:mb3="h [...]
+    <mb3:primary>
+        <mb3:menu>
+            <mb3:named>Simple Objects</mb3:named>
+            <mb3:section>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="create"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="findByName"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="findByNameLike"/>
+                <mb3:serviceAction objectType="simple.SimpleObjects" id="listAll"/>
+            </mb3:section>
+        </mb3:menu>
+        <mb3:menu unreferencedActions="true">
+            <mb3:named>Other</mb3:named>
+        </mb3:menu>
+    </mb3:primary>
+    <mb3:secondary>
+        <mb3:menu>
+            <mb3:named>Prototyping</mb3:named>
+            <mb3:section>
+                <mb3:named>Fixtures</mb3:named>
+                <mb3:serviceAction objectType="isis.testing.fixtures.FixtureScripts" id="runFixtureScript"/>
+                <mb3:serviceAction objectType="isis.testing.fixtures.FixtureScripts" id="recreateObjectsAndReturnFirst"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Layouts</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.LayoutServiceMenu" id="downloadLayouts"/>
+                <mb3:serviceAction objectType="isis.applib.LayoutServiceMenu" id="downloadMenuBarsLayout"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Meta Model and Features</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelXml"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelCsv"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelAscii"/>
+                <mb3:serviceAction objectType="isis.applib.MetaModelServiceMenu" id="downloadMetaModelDiff"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allNamespaces"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allTypes"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allActions"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allProperties"/>
+                <mb3:serviceAction objectType="isis.feat.ApplicationFeatureMenu" id="allCollections"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Persistence</mb3:named>
+                <mb3:serviceAction objectType="isis.persistence.jdo.JdoMetamodelMenu" id="downloadMetamodels"/>
+                <mb3:serviceAction objectType="isis.ext.h2Console.H2ManagerMenu" id="openH2Console"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>REST API</mb3:named>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="openSwaggerUi"/>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="openRestApi"/>
+                <mb3:serviceAction objectType="isis.viewer.restfulobjects.SwaggerServiceMenu" id="downloadSwaggerSchemaDefinition"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>i18n</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="downloadTranslations"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="resetTranslationCache"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="switchToReadingTranslations"/>
+                <mb3:serviceAction objectType="isis.applib.TranslationServicePoMenu" id="switchToWritingTranslations"/>
+            </mb3:section>
+        </mb3:menu>
+    </mb3:secondary>
+    <mb3:tertiary>
+        <mb3:menu>
+            <mb3:named/>
+            <mb3:section>
+                <mb3:named>Configuration</mb3:named>
+                <mb3:serviceAction objectType="isis.conf.ConfigurationMenu" id="configuration"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Impersonate</mb3:named>
+                <mb3:serviceAction objectType="isis.sudo.ImpersonateMenu" id="impersonate"/>
+                <mb3:serviceAction objectType="isis.sudo.ImpersonateMenu" id="impersonateWithRoles"/>
+                <mb3:serviceAction objectType="isis.applib.ImpersonateStopMenu" id="stopImpersonating"/>
+            </mb3:section>
+            <mb3:section>
+                <mb3:named>Security</mb3:named>
+                <mb3:serviceAction objectType="isis.applib.UserMenu" id="me"/>
+                <mb3:serviceAction objectType="isis.security.LogoutMenu" id="logout"/>
+            </mb3:section>
+        </mb3:menu>
+    </mb3:tertiary>
+</mb3:menuBars>
diff --git a/webapp/src/main/resources/shiro.ini b/webapp/src/main/resources/shiro.ini
new file mode 100644
index 0000000..59e43ba
--- /dev/null
+++ b/webapp/src/main/resources/shiro.ini
@@ -0,0 +1,53 @@
+[main]
+
+# to use .ini file
+securityManager.realms = $iniRealm
+
+
+
+# -----------------------------------------------------------------------------
+# Users and their assigned roles
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+
+[users]
+# user = password, role1, role2, role3, ...
+
+
+sven = pass, admin_role
+dick = pass, simple_role, default_role
+bob  = pass, simple_role, default_role, fixtures_role
+joe  = pass, simple_role, default_role
+guest = guest, guest_role, default_role
+
+
+
+# -----------------------------------------------------------------------------
+# Roles with assigned permissions
+#
+# Each line conforms to the format defined in the
+# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
+# -----------------------------------------------------------------------------
+
+[roles]
+# role = perm1, perm2, perm3, ...
+# perm in format: logicalTypeNamespace:logicalTypeSimpleName:memberName:r,w
+
+simple_role    = *:SimpleObjects:*:*,\
+                 *:SimpleObject:*:*
+guest_role     = *:SimpleObjects:findByName:*,\
+                 *:SimpleObjects:listAll:*,\
+                 *:SimpleObjects:*:r
+admin_role     = *
+default_role   = isis.applib,\
+                 isis.security
+fixtures_role  = isis.testing.fixtures
+features_role  = isis.feat
+metamodel_role = isis.metamodel
+h2_role        = isis.ext.h2Console
+jdo_role       = isis.persistence.jdo
+swagger_role   = isis.viewer.restfulobjects
+conf_role      = isis.conf
+sudo_role      = isis.sudo
diff --git a/webapp/src/main/resources/static/css/application.css b/webapp/src/main/resources/static/css/application.css
new file mode 100644
index 0000000..e69de29
diff --git a/webapp/src/main/resources/static/css/page.css b/webapp/src/main/resources/static/css/page.css
new file mode 100644
index 0000000..487d6a8
--- /dev/null
+++ b/webapp/src/main/resources/static/css/page.css
@@ -0,0 +1,27 @@
+body {
+    background-color: #1A467B;
+    font-family: Verdana, Helvetica, Arial, serif;
+    font-size: 90%;
+}
+
+li {
+    margin-top: 6px;
+    margin-bottom: 6px;
+}
+table {
+    border-collapse: collapse;
+}
+table, th, td {
+    border: 1px;
+    border-style: solid;
+    border-color: lightgray;
+}
+th, td {
+    padding: 10px;
+}
+#wrapper {
+    background-color: #ffffff;
+    width: 900px;
+    margin: 8px auto;
+    padding: 12px;
+}
diff --git a/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png b/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png
new file mode 100644
index 0000000..08e012c
Binary files /dev/null and b/webapp/src/main/resources/static/images/apache-isis/logo-48x48.png differ
diff --git a/webapp/src/main/resources/static/images/apache-isis/logo.png b/webapp/src/main/resources/static/images/apache-isis/logo.png
new file mode 100644
index 0000000..5284fe7
Binary files /dev/null and b/webapp/src/main/resources/static/images/apache-isis/logo.png differ
diff --git a/webapp/src/main/resources/static/images/favicon.png b/webapp/src/main/resources/static/images/favicon.png
new file mode 100644
index 0000000..2586589
Binary files /dev/null and b/webapp/src/main/resources/static/images/favicon.png differ
diff --git a/webapp/src/main/resources/static/index.html b/webapp/src/main/resources/static/index.html
new file mode 100644
index 0000000..5e8c12d
--- /dev/null
+++ b/webapp/src/main/resources/static/index.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+        <title>Apache Isis&trade; Simple App</title>
+
+        <link rel="stylesheet" type="text/css" href="css/page.css">
+    </head>
+    <body>
+        <div id="wrapper">
+            <img alt="Isis Logo" src="images/apache-isis/logo.png" />
+
+            <p>
+                This is a simple <a href="http://isis.apache.org">Apache Isis</a> application, structured so it can be
+                used as a starting point for developing your own applications.
+                <br/>
+            </p>
+
+            <p>To access the app:</p>
+            <ul>
+                <li>
+                    <p>
+                        <b><a href="wicket/">Generic UI (Wicket)</a></b>
+                    </p>
+                    <p>
+                        provides access to a generic UI for end-users.  This
+                        viewer is built with <a href="http://wicket.apache.org" target="_blank">Apache Wicket</a>&trade;.
+                    </p>
+                </li>
+                <li>
+                    <p>
+                        <b>
+                            <a href="swagger-ui/index.thtml">RESTful API (Swagger)</a>
+                        </b>
+                    </p>
+                    <p>
+                        provides access to a Swagger UI for convenient access
+                        to (a subset of) the automatically generated REST API.
+                    </p>
+                    <p>
+                        The full backend API (at <a href="restful/">restful/</a>) renders both simple and also richer
+                        hypermedia representations of domain objects, the latter conforming to the
+                        <a href="http://restfulobjects.org"  target="_blank">Restful Objects</a> spec.
+                    </p>
+                </li>
+            </ul>
+
+            <p>
+            The default user/password is <b><i>sven/pass</i></b>.
+            </p>
+
+        </div>
+    </body>
+</html>
diff --git a/webapp/src/main/resources/static/scripts/application.js b/webapp/src/main/resources/static/scripts/application.js
new file mode 100644
index 0000000..d8cf6fe
--- /dev/null
+++ b/webapp/src/main/resources/static/scripts/application.js
@@ -0,0 +1,3 @@
+$(document).ready(function() {
+	/// here...
+});
\ No newline at end of file
diff --git a/webapp/src/main/resources/templates/error.html b/webapp/src/main/resources/templates/error.html
new file mode 100644
index 0000000..f9d0a41
--- /dev/null
+++ b/webapp/src/main/resources/templates/error.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html xmlns:th="http://www.thymeleaf.org">
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+        <title>Apache Isis&trade; Simple App</title>
+
+        <link rel="stylesheet" type="text/css" th:href="@{/css/page.css}">
+    </head>
+    <body>
+        <div id="wrapper">
+            <img alt="Isis Logo" th:src="@{/images/apache-isis/logo.png}" />
+             
+            <p>
+                Our apologies - an error occurred.
+            </p>
+
+            <p>You can access the app <a th:href="@{/wicket/}">here</a>
+        </div>
+    </body>
+</html>
diff --git a/webapp/translations.po b/webapp/translations.po
new file mode 100644
index 0000000..e69de29