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™ 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>™.
+ </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™ 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