http://git-wip-us.apache.org/repos/asf/isis-site/blob/fcfba6ed/content/versions/SNAPSHOT/pages/tg/tg.html ---------------------------------------------------------------------- diff --git a/content/versions/SNAPSHOT/pages/tg/tg.html b/content/versions/SNAPSHOT/pages/tg/tg.html index 4610f45..0c1ff0d 100644 --- a/content/versions/SNAPSHOT/pages/tg/tg.html +++ b/content/versions/SNAPSHOT/pages/tg/tg.html @@ -26,7 +26,7 @@ <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="expires" content="-1"> - <title>Tutorials</title> + <title>Tutorial</title> <link rel="icon" type="image/png" href="../../images/isis-favicon.png"> <!-- Based on DataNucleus' template, @@ -243,6 +243,7 @@ table.CodeRay td.code>pre{padding:0} </div> </form> </div> + <p class="nav navbar-text navbar-right small">v1.16.1-SNAPSHOT</p> </div> </div> </nav> @@ -257,7 +258,7 @@ table.CodeRay td.code>pre{padding:0} </div> </div> <div class="page-title"> - <h1>Tutorials</h1> + <h1>Tutorial</h1> </div> <div id="doc-content"> <div class="btn-group" style="float: right; font-size: small; padding: 6px; "> @@ -270,1773 +271,988 @@ table.CodeRay td.code>pre{padding:0} <li><a href="https://github.com/apache/isis/blame/master/adocs/documentation/src/main/asciidoc/pages/tg/tg.adoc" target="_blank"><i class="fa fa-hand-o-right fa-fw" aria-hidden="true"></i> Blame</a></li> </ul> </div> + <div id="preamble"> + <div class="sectionbody"> + <div class="paragraph"> + <p>This is a half-day tutorial on developing domain-driven apps using Apache Isis.</p> + </div> + <div class="paragraph"> + <p>Actually, you could probably spend a full day working through this tutorial if you wanted to, so pick and choose the bits that look interesting. The tutorial was originally written by Dan Haywood, for a conference workshop.</p> + </div> + </div> + </div> <div class="sect1"> - <h2 id="__tg">1. Tutorials</h2> + <h2 id="_prerequisites">1. Prerequisites</h2> <div class="sectionbody"> <div class="paragraph"> - <p>This page contains a couple of tutorials for you to follow.</p> + <p>Youâll need:</p> </div> <div class="ulist"> <ul> - <li> <p>the <a href="#_tg_tutorials_pet-clinic">"petclinic"</a> tutorial takes you step-by-step through building a simple application of just three classes. There are example solutions in the github repo in case you get lost.</p> </li> - <li> <p>an <a href="#_tg_tutorials_pet-clinic-extended">extended version</a> of the "petclinic" tutorial, (with the text hosted on github repo).</p> </li> - <li> <p>the <a href="#_tg_tutorials_stop-scaffolding-start-coding">"stop scaffolding, start coding"</a> tutorial is taken from a conference workshop. It has less hand-holding, but lists out the steps for you to follow. Itâs a good cookbook to follow when youâre readng to take things further.</p> </li> + <li> <p>Java 7 JDK</p> </li> + <li> <p><a href="http://maven.apache.org/">Maven</a> 3.2.x</p> </li> + <li> <p>an IDE, such as <a href="http://www.eclipse.org/">Eclipse</a> or <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.</p> </li> </ul> </div> + </div> + </div> + <div class="sect1"> + <h2 id="_run_the_archetype">2. Run the archetype</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Run the simpleapp archetype to build an empty Isis application. With the *nix bash shell, use:</p> + </div> + <div class="listingblock"> + <div class="content"> + <pre class="CodeRay highlight"><code data-lang="bash">mvn archetype:generate \ + -D archetypeGroupId=org.apache.isis.archetype \ + -D archetypeArtifactId=simpleapp-archetype \ + -D archetypeVersion=1.16.0 \ + -D groupId=com.mycompany \ + -D artifactId=myapp \ + -D version=1.0-SNAPSHOT \ + -D archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \ + -B</code></pre> + </div> + </div> <div class="paragraph"> - <p>Have fun!</p> + <p>Adjust as necessary if using Windows <code>cmd.exe</code> or Powershell.</p> </div> </div> </div> <div class="sect1"> - <h2 id="_tg_pet-clinic">2. Pet Clinic</h2> - <div class="btn-group" style="float: right; font-size: small; padding: 6px; margin-top: -55px; "> - <button type="button" class="btn btn-xs btn-default" onclick="window.location.href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic.adoc""><i class="fa fa-pencil-square-o"></i> Edit</button> - <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span><span class="sr-only">Toggle Dropdown</span></button> - <ul class="dropdown-menu"> - <li><a href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic.adoc" target="_blank"><i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> Edit</a></li> - <li><a href="https://github.com/apache/isis/commits/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic.adoc" target="_blank"><i class="fa fa-clock-o fa-fw" aria-hidden="true"></i> History</a></li> - <li><a href="https://github.com/apache/isis/raw/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic.adoc" target="_blank"><i class="fa fa-file-text-o fa-fw" aria-hidden="true"></i> Raw</a></li> - <li><a href="https://github.com/apache/isis/blame/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic.adoc" target="_blank"><i class="fa fa-hand-o-right fa-fw" aria-hidden="true"></i> Blame</a></li> - </ul> - </div> + <h2 id="_build_and_run">3. Build and run</h2> <div class="sectionbody"> <div class="paragraph"> - <p>This is a step-by-step tutorial to build up a simple "petclinic" application, starting from the <a href="../../guides/ugfun/ugfun.html#_ugfun_getting-started_simpleapp-archetype">SimpleApp archetype</a>. It was originally written by Jeroen van der Wal.</p> + <p>Start off by building the app from the command line:</p> + </div> + <div class="listingblock"> + <div class="content"> + <pre class="CodeRay highlight"><code data-lang="bash">cd myapp +mvn clean install -D mavenmixin-jettyconsole</code></pre> + </div> + </div> + <div class="paragraph"> + <p>Once thatâs built then run using:</p> + </div> + <div class="listingblock"> + <div class="content"> + <pre class="CodeRay highlight"><code data-lang="bash">mvn -pl webapp antrun:run -D mavenmixin-jettyconsole</code></pre> + </div> + </div> + <div class="paragraph"> + <p>A splash screen should appear offering to start up the app. Go ahead and start; the web browser should be opened at <a href="http://localhost:8080">http://localhost:8080</a></p> </div> <div class="paragraph"> - <p>It consists of just three domain classes (<a href="http://yuml.me/edit/3db2078c">http://yuml.me/3db2078c</a>):</p> + <p>Alternatively, you can run using the mvn-jetty-plugin:</p> </div> - <div class="imageblock"> + <div class="listingblock"> <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/domain-model.png"><img src="images/tutorials/pet-clinic/domain-model.png" alt="domain model"></a> + <pre class="CodeRay highlight"><code data-lang="bash">mvn -pl webapp jetty:run</code></pre> </div> </div> <div class="paragraph"> - <p>This supports the following use cases:</p> + <p>This will accomplish the same thing, though the webapp is mounted at a slightly different URL</p> + </div> + </div> + </div> + <div class="sect1"> + <h2 id="_using_the_app">4. Using the app</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Navigate to the Wicket UI (eg <a href="http://localhost:8080/wicket">http://localhost:8080/wicket</a>), and login (sven/pass).</p> + </div> + <div class="paragraph"> + <p>Once at the home page:</p> </div> <div class="ulist"> <ul> - <li> <p>register a <code>Pet</code></p> </li> - <li> <p>register an <code>Owner</code></p> </li> - <li> <p>maintain a <code>Pet</code>'s details</p> </li> - <li> <p>check in a <code>Pet</code> to visit the clinic</p> </li> - <li> <p>enter a <code>Diagnosis</code></p> </li> - <li> <p>check out a <code>Pet</code> after visiting the clinic</p> </li> + <li> <p>install fixtures</p> </li> + <li> <p>list all objects</p> </li> + <li> <p>create a new object</p> </li> + <li> <p>list all objects</p> </li> </ul> </div> <div class="paragraph"> - <p>Either follow along or check out the tags from the corresponding <a href="https://github.com/danhaywood/isis-app-petclinic">github repo</a>.</p> + <p>Go back to the splash screen, and quit the app. Note that the database runs in-memory (using HSQLDB) so any data created will be lost between runs.</p> </div> - <div class="sect2"> - <h3 id="_prerequisites">2.1. Prerequisites</h3> - <div class="paragraph"> - <p>Youâll need:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>Java 7 JDK</p> </li> - <li> <p><a href="http://maven.apache.org/">Maven</a> 3.2.x</p> </li> - <li> <p>an IDE, such as <a href="http://www.eclipse.org/">Eclipse</a> or <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.</p> </li> - </ul> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_dev_environment">5. Dev environment</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Set up <a href="../../guides/dg/dg.html#_dg_ide">an IDE</a> and import the project to be able to run and debug the app.</p> </div> - <div class="sect2"> - <h3 id="_run_the_archetype">2.2. Run the archetype</h3> - <div class="paragraph"> - <p>Throughout this tutorial you can, if you wish, just checkout from the github repo wherever you see a "git checkout" note:</p> - </div> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/249abe476797438d83faa12ff88365da2c362451</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Do note that the tutorial was originally developed against Apache Isis 1.8.0-SNAPSHOT; replace "1.8.0-SNAPSHOT" for the any version from 1.8.0 up to 1.13.1. (The structure of Apache Isis archetype changed in 1.13.2).</p> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>Run the simpleapp archetype to build an empty Isis application. With the *nix bash shell, use:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn archetype:generate \ - -D archetypeGroupId=org.apache.isis.archetype \ - -D archetypeArtifactId=simpleapp-archetype \ - -D archetypeVersion=1.13.1 \ - -D groupId=com.mycompany \ - -D artifactId=petclinic \ - -D version=1.0-SNAPSHOT \ - -D archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \ - -B</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Adjust as necessary if using Windows <code>cmd.exe</code> or Powershell.</p> - </div> - <div class="admonitionblock note"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-note" title="Note"></i> </td> - <td class="content"> - <div class="paragraph"> - <p>You can use 1.13.2 (or later) version of the archetype if you wish, however be aware that the structure of the solution repo (mentioned above) will be different from the structure for applications generated using 1.13.2 onwards.</p> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>This will generate the app in a <code>petclinic</code> directory. Move the contents back:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mv petclinic/* . -rmdir petclinic</code></pre> - </div> - </div> + <div class="paragraph"> + <p>Then set up a launch configuration and check that you can:</p> </div> - <div class="sect2"> - <h3 id="_build_and_run">2.3. Build and run</h3> - <div class="paragraph"> - <p>Start off by building the app from the command line:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn clean install</code></pre> - </div> - </div> - <div class="paragraph"> - <p>You can run using the mvn-jetty-plugin:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn -pl webapp jetty:run</code></pre> - </div> - </div> - <div class="paragraph"> - <p>You can then browser to <a href="http://localhost:8080">http://localhost:8080</a></p> - </div> - <div class="paragraph"> - <p>If using 1.13.2 or later, then you can also use the <a href="https://github.com/danhaywood/java-mavenmixin-jettyconsole/blob/master/README.adoc">jetty-console</a> plugin:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn clean install -Dmavenmixin-jettyconsole -mvn -pl webapp antrun:run -Dmavenmixin-jettyconsole</code></pre> - </div> - </div> - <div class="paragraph"> - <p>This packages the app as a standalone JAR, and then runs using the AntRun plugin.</p> - </div> + <div class="ulist"> + <ul> + <li> <p>Run the app from within the IDE</p> </li> + <li> <p>Run the app in debug mode</p> </li> + <li> <p>Run with different deploymentTypes; note whether prototype actions (those annotated <a href="../../guides/rgant/rgant.html#_rgant-Action_restrictTo"><code>@Action(restrictTo=PROTOTYPING</code></a>) are available or not:</p> </li> + <li> <p><code>--type SERVER_PROTOTYPE</code></p> </li> + <li> <p><code>--type SERVER</code></p> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_using_the_app">2.4. Using the app</h3> - <div class="paragraph"> - <p>Navigate to the Wicket UI (eg <a href="http://localhost:8080/wicket">http://localhost:8080/wicket</a>), and login (sven/pass).</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/010-01-login-page.png"><img src="images/tutorials/pet-clinic/010-01-login-page.png" alt="010 01 login page" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>The home page should be shown:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/010-02-home-page.png"><img src="images/tutorials/pet-clinic/010-02-home-page.png" alt="010 02 home page" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>Install the fixtures (example test data) using the <code>Prototyping</code> menu:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/010-03-prototyping-menu.png"><img src="images/tutorials/pet-clinic/010-03-prototyping-menu.png" alt="010 03 prototyping menu" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>List all objects using the <code>Simple Objects</code> menu:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/010-04-simpleobjects.png"><img src="images/tutorials/pet-clinic/010-04-simpleobjects.png" alt="010 04 simpleobjects" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>To return the objects created:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/010-05-simpleobject-list.png"><img src="images/tutorials/pet-clinic/010-05-simpleobject-list.png" alt="010 05 simpleobject list" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>Experiment some more, to:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>create a new object</p> </li> - <li> <p>list all objects</p> </li> - </ul> - </div> - <div class="paragraph"> - <p>Go back to the splash screen, and quit the app. Note that the database runs in-memory (using HSQLDB) so any data created will be lost between runs.</p> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_explore_codebase">6. Explore codebase</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Apache Isis applications are organized into several Maven modules. Within your IDE navigate to the various classes and correlate back to the generated UI.</p> </div> - <div class="sect2"> - <h3 id="_dev_environment">2.5. Dev environment</h3> - <div class="paragraph"> - <p>Set up <a href="../../guides/dg/dg.html#_dg_ide">an IDE</a> and import the project to be able to run and debug the app.</p> - </div> - <div class="paragraph"> - <p>Then set up a launch configuration so that you can run the app from within the IDE. To save having to run the fixtures every time, specify the following system properties:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">-Disis.persistor.datanucleus.install-fixtures=true -Disis.fixtures=fixture.simple.scenario.SimpleObjectsFixture</code></pre> - </div> - </div> - <div class="paragraph"> - <p>For example, hereâs what a launch configuration in IntelliJ idea looks like:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/020-01-idea-configuration.png"><img src="images/tutorials/pet-clinic/020-01-idea-configuration.png" alt="020 01 idea configuration" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>where the "before launch" maven goal (to run the DataNucleus enhancer) is defined as:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/020-02-idea-configuration.png"><img src="images/tutorials/pet-clinic/020-02-idea-configuration.png" alt="020 02 idea configuration" width="400px"></a> - </div> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_testing">7. Testing</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Testing is of course massively important, and Apache Isis makes both unit testing and (end-to-end) integration testing easy. Building the app from the Maven command line ("mvn clean install") will run all tests, but you should also run the tests from within the IDE.</p> </div> - <div class="sect2"> - <h3 id="_explore_codebase">2.6. Explore codebase</h3> - <div class="paragraph"> - <p>Apache Isis applications are organized into several Maven modules. Within your IDE navigate to the various classes and correlate back to the generated UI:</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>petclinic</code> : parent module</p> </li> - <li> <p><code>petclinic-dom</code>: domain objects module</p> - <div class="ulist"> - <ul> - <li> <p>entity: <code>dom.simple.SimpleObject</code></p> </li> - <li> <p>repository: <code>dom.simple.SimpleObjects</code></p> </li> - </ul> - </div> </li> - <li> <p><code>petclinic-fixture</code>: fixtures module</p> - <div class="ulist"> - <ul> - <li> <p>fixture script:`fixture.simple.SimpleObjectsFixture`</p> </li> - </ul> - </div> </li> - <li> <p><code>petclinic-integtests</code>: integration tests module</p> </li> - <li> <p><code>petclinic-webapp</code>: webapp module</p> - <div class="ulist"> - <ul> - <li> <p>(builds the WAR file)</p> </li> - </ul> - </div> </li> - </ul> - </div> + <div class="ulist"> + <ul> + <li> <p><code>myapp-dom</code> unit tests</p> </li> + <li> <p>run</p> </li> + <li> <p>inspect, eg</p> + <div class="ulist"> + <ul> + <li> <p><code>SimpleObjectTest</code></p> </li> + </ul> + </div> </li> + <li> <p><code>myapp-integtests</code> integration tests</p> </li> + <li> <p>run</p> </li> + <li> <p>inspect, eg:</p> + <div class="ulist"> + <ul> + <li> <p><code>integration.tests.smoke.SimpleObjectsTest</code></p> </li> + <li> <p><code>integration.specs.simple.SimpleObjectSpec_listAllAndCreate.feature</code></p> </li> + </ul> + </div> </li> + <li> <p>generated report, eg</p> + <div class="ulist"> + <ul> + <li> <p><code>myapp/integtests/target/cucumber-html-report/index.html</code></p> </li> + <li> <p>change test in IDE, re-run (in Maven)</p> </li> + </ul> + </div> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_testing">2.7. Testing</h3> - <div class="paragraph"> - <p>Testing is of course massively important, and Apache Isis makes both unit testing and (end-to-end) integration testing easy. Building the app from the Maven command line ("mvn clean install") will run all tests, but you should also run the tests from within the IDE.</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>myapp-dom</code> unit tests</p> </li> - <li> <p>run</p> </li> - <li> <p>inspect, eg</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleObjectTest</code></p> </li> - </ul> - </div> </li> - <li> <p><code>myapp-integtests</code> integration tests</p> </li> - <li> <p>run</p> </li> - <li> <p>inspect, eg:</p> - <div class="ulist"> - <ul> - <li> <p><code>integration.tests.smoke.SimpleObjectsTest</code></p> </li> - <li> <p><code>integration.specs.simple.SimpleObjectSpec_listAllAndCreate.feature</code></p> </li> - </ul> - </div> </li> - <li> <p>generated report, eg</p> - <div class="ulist"> - <ul> - <li> <p><code>myapp/integtests/target/cucumber-html-report/index.html</code></p> - <div class="ulist"> - <ul> - <li> <p>change test in IDE, re-run (in Maven)</p> </li> - </ul> - </div> </li> - </ul> - </div> </li> - </ul> - </div> - <div class="paragraph"> - <p>If you have issues with the integration tests, make sure that the domain classes have been enhanced by the DataNucleus enhancer. (The exact mechanics depends on the IDE being used).</p> - </div> + <div class="paragraph"> + <p>If you have issues with the integration tests, make sure that the domain classes have been enhanced by the DataNucleus enhancer. (The exact mechanics depends on the IDE being used).</p> </div> - <div class="sect2"> - <h3 id="_update_pom_files">2.8. Update POM files</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/68904752bc2de9ebb3c853b79236df2b3ad2c944</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>The POM files generated by the simpleapp archetype describe the app as "SimpleApp". Update them to say "PetClinic" instead.</p> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_prototyping">8. Prototyping</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Although testing is important, in this tutorial we want to concentrate on how to write features and to iterate quickly. So for now, exclude the <code>integtests</code> module. Later on in the tutorial weâll add the tests back in so you can learn how to write automated tests for the features of your app.</p> </div> - <div class="sect2"> - <h3 id="_delete_the_bdd_specs">2.9. Delete the BDD specs</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/9046226249429b269325dfa2baccf03635841c20</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>During this tutorial weâre going to keep the integration tests in-sync with the code, but weâre going to stop short of writing BDD/Cucumber specs.</p> - </div> - <div class="paragraph"> - <p>Therefore delete the BDD feature spec and glue in the <code>integtest</code> module:</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>integration/specs/*</code></p> </li> - <li> <p><code>integration/glue/*</code></p> </li> - </ul> - </div> + <div class="paragraph"> + <p>In the parent <code>pom.xml</code>:</p> </div> - <div class="sect2"> - <h3 id="_rename_artifacts">2.10. Rename artifacts</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/bee3629c0b64058f939b6dd20f226be31810fc66</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> + <div class="listingblock"> + <div class="content"> + <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><modules></span> + <span class="tag"><module></span>dom<span class="tag"></module></span> + <span class="tag"><module></span>fixture<span class="tag"></module></span> + <span class="tag"><module></span>integtests<span class="tag"></module></span> + <span class="tag"><module></span>webapp<span class="tag"></module></span> +<span class="tag"></modules></span></code></pre> </div> - <div class="paragraph"> - <p>Time to start refactoring the app. The heart of the PetClinic app is the <code>Pet</code> concept, so go through the code and refactor. While weâre at it, refactor the app itself from "SimpleApp" to "PetClinicApp".</p> - </div> - <div class="paragraph"> - <p>See the git commit for more detail, but in outline, the renames required are:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>in the <code>dom</code> moduleâs production code</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleObject</code> -> <code>Pet</code> (entity)</p> </li> - <li> <p><code>SimpleObjects</code> -> <code>Pets</code> (repository domain service)</p> </li> - <li> <p><code>SimpleObject.layout.json</code> -> <code>Pet.layout.json</code> (layout hints for the <code>Pet</code> entity)</p> </li> - <li> <p>delete the <code>SimpleObject.png</code>, and add a new <code>Pet.png</code> (icon shown against all <code>Pet</code> instances).</p> </li> - </ul> - </div> </li> - <li> <p>in the <code>dom</code> moduleâs unit test code</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleObjectTest</code> -> <code>PetTest</code> (unit tests for <code>Pet</code> entity)</p> </li> - <li> <p><code>SimpleObjectsTest</code> -> <code>PetsTest</code> (unit tests for <code>Pets</code> domain service)</p> </li> - </ul> - </div> </li> - <li> <p>in the <code>fixture</code> module:</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleObjectsFixturesService</code> -> <code>PetClinicAppFixturesService</code> (rendered as the prototyping menu in the UI)</p> </li> - <li> <p><code>SimpleObjectsTearDownService</code> -> <code>PetClinicAppTearDownService</code> (tear down all objects between integration tests)</p> </li> - <li> <p><code>SimpleObjectAbstract</code> -> <code>PetAbstract</code> (abstract class for setting up a single pet object</p> - <div class="ulist"> - <ul> - <li> <p>and corresponding subclasses to set up sample data (eg <code>PetForFido</code>)</p> </li> - </ul> - </div> </li> - <li> <p><code>SimpleObjectsFixture</code> -> <code>PetsFixture</code> (tear downs system and then sets up all pets)</p> </li> - </ul> - </div> </li> - <li> <p>in the <code>integtest</code> module:</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleAppSystemInitializer</code> -> <code>PetClinicAppSystemInitializer</code> (bootstraps integration tests with domain service/repositories)</p> </li> - <li> <p><code>SimpleAppIntegTest</code> -> <code>PetClinicAppIntegTest</code> (base class for integration tests)</p> </li> - <li> <p><code>SimpleObjectTest</code> -> <code>PetTest</code> (integration test for <code>Pet</code> entity)</p> </li> - <li> <p><code>SimpleObjectsTest</code> -> <code>PetsTest</code> (integration test for <code>Pets</code> domain service)</p> </li> - </ul> - </div> </li> - <li> <p>in the <code>webapp</code> module:</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleApplication</code> -> <code>PetClinicApplication</code></p> </li> - <li> <p>update <code>isis.properties</code></p> </li> - <li> <p>update <code>web.xml</code></p> </li> - </ul> - </div> </li> - </ul> - </div> - <div class="paragraph"> - <p>Note that <code>Pet</code> has both both Isis and JDO annotations:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@javax</span>.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE) <i class="conum" data-value="1"></i><b>(1)</b> -<span class="annotation">@javax</span>.jdo.annotations.DatastoreIdentity( <i class="conum" data-value="2"></i><b>(2)</b> - strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY, - column=<span class="string"><span class="delimiter">"</span><span class="content">id</span><span class="delimiter">"</span></span>) -<span class="annotation">@javax</span>.jdo.annotations.Version( <i class="conum" data-value="3"></i><b>(3)</b> - strategy=VersionStrategy.VERSION_NUMBER, - column=<span class="string"><span class="delimiter">"</span><span class="content">version</span><span class="delimiter">"</span></span>) -<span class="annotation">@javax</span>.jdo.annotations.Unique(name=<span class="string"><span class="delimiter">"</span><span class="content">Pet_name_UNQ</span><span class="delimiter">"</span></span>, members = {<span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>}) <i class="conum" data-value="4"></i><b>(4)</b> -<span class="annotation">@ObjectType</span>(<span class="string"><span class="delimiter">"</span><span class="content">PET</span><span class="delimiter">"</span></span>) <i class="conum" data-value="5"></i><b>(5)</b> -<span class="annotation">@Bookmarkable</span> <i class="conum" data-value="6"></i><b>(6)</b> -<span class="directive">public</span> <span class="type">class</span> <span class="class">Pet</span> <span class="directive">implements</span> <span class="predefined-type">Comparable</span><Pet> { - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>where:</p> - </div> - <div class="colist arabic"> - <table> - <tbody> - <tr> - <td><i class="conum" data-value="1"></i><b>1</b></td> - <td><code>@PersistenceCapable</code> and</td> - </tr> - <tr> - <td><i class="conum" data-value="2"></i><b>2</b></td> - <td><code>@DatastoreIdentity</code> specify a surrogate <code>Id</code> column to be used as the primary key</td> - </tr> - <tr> - <td><i class="conum" data-value="3"></i><b>3</b></td> - <td><code>@Version</code> provides support for optimistic locking</td> - </tr> - <tr> - <td><i class="conum" data-value="4"></i><b>4</b></td> - <td><code>@Unique</code> enforces a uniqueness constraint so that no two `Pet`s can have the same name (unrealistic, but can refactor later)</td> - </tr> - <tr> - <td><i class="conum" data-value="5"></i><b>5</b></td> - <td><code>@ObjectType</code> is used by Apache Isis for its own internal "OID" identifier; this also appears in the URL in Apache Isis' Wicket viewer and REST API</td> - </tr> - <tr> - <td><i class="conum" data-value="6"></i><b>6</b></td> - <td><code>@Bookmarkable</code> indicates that the object can be automatically bookmarked in Apache Isis' Wicket viewer</td> - </tr> - </tbody> - </table> - </div> - <div class="admonitionblock note"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-note" title="Note"></i> </td> - <td class="content"> - <div class="paragraph"> - <p>The <code>@ObjectType</code> and <code>@Bookmarkable</code> annotations have since been deprecated, replaced with <code>@DomainObject(objectType=â¦â)</code> and <code>@DomainObjectLayout(bookmarking=â¦â)</code></p> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>The <code>Pets</code> domain service also has Isis annotations:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="annotation">@DomainService</span>(repositoryFor = Pet.class) -<span class="annotation">@DomainServiceLayout</span>(menuOrder = <span class="string"><span class="delimiter">"</span><span class="content">10</span><span class="delimiter">"</span></span>) -<span class="directive">public</span> <span class="type">class</span> <span class="class">Pets</span> { - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>where:</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>DomainService</code> indicates that the service should be instantiated automatically (as a singleton)</p> </li> - <li> <p><code>DomainServiceLayout</code> provides UI hints, in this case the positioning of the menu for the actions provided by the service</p> </li> - </ul> - </div> - <div class="paragraph"> - <p>To run the application will require an update to the IDE configuration, for the changed name of the fixture class:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/030-01-idea-configuration-updated.png"><img src="images/tutorials/pet-clinic/030-01-idea-configuration-updated.png" alt="030 01 idea configuration updated" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>Running the app should now show `Pet`s:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/030-02-updated-app.png"><img src="images/tutorials/pet-clinic/030-02-updated-app.png" alt="030 02 updated app" width="600px"></a> - </div> + </div> + <div class="paragraph"> + <p>change to:</p> + </div> + <div class="listingblock"> + <div class="content"> + <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag"><modules></span> + <span class="tag"><module></span>dom<span class="tag"></module></span> + <span class="tag"><module></span>fixture<span class="tag"></module></span> + <span class="comment"><!-- <module>integtests</module> --></span> + <span class="tag"><module></span>webapp<span class="tag"></module></span> +<span class="tag"></modules></span></code></pre> </div> </div> - <div class="sect2"> - <h3 id="_update_package_names">2.11. Update package names</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/55ec36e520191f5fc8fe7f5b89956814eaf13317</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>The classes created by the simpleapp archetype are by default in the <code>simple</code> package. Move these classes to <code>pets</code> package instead. Also adjust package names where they appear as strings:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>in <code>PetClinicAppFixturesService</code>, change the package name from "fixture.simple" to "fixture.pets".</p> </li> - <li> <p>in <code>PetClinicAppSystemInitializer</code>, change the package name "dom.simple" to "dom.pets", and similarly "fixture.simple" to "fixture.pets"</p> </li> - <li> <p>in <code>WEB-INF/isis.properties</code>, similarly change the package name "dom.simple" to "dom.pets", and similarly "fixture.simple" to "fixture.pets"</p> </li> - </ul> - </div> - <div class="paragraph"> - <p>To run the application will require a further update to the IDE configuration, for the changed package of the fixture class:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/040-01-idea-configuration-updated.png"><img src="images/tutorials/pet-clinic/040-01-idea-configuration-updated.png" alt="040 01 idea configuration updated" width="600px"></a> - </div> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_build_a_domain_app">9. Build a domain app</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>The remainder of the tutorial provides guidance on building a domain application. We donât mandate any particular design, but we suggest one with no more than 3 to 6 domain entities in the first instance. If youâre stuck for ideas, then how about:</p> </div> - <div class="sect2"> - <h3 id="_add_code_petspecies_code_enum">2.12. Add <code>PetSpecies</code> enum</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/55c9cd28ff960220719b3dc7cb8abadace8d0829</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>Each <code>Pet</code> is of a particular species. Model these as an enum called <code>PetSpecies</code>:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">enum</span> PetSpecies { - Cat, - Dog, - Budgie, - Hamster, - Tortoise -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Introduce a new property on <code>Pet</code> of this type:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">Pet</span> { - ... - private PetSpecies species; - <span class="annotation">@javax</span>.jdo.annotations.Column(allowsNull = <span class="string"><span class="delimiter">"</span><span class="content">false</span><span class="delimiter">"</span></span>) - <span class="directive">public</span> PetSpecies getSpecies() { <span class="keyword">return</span> species; } - <span class="directive">public</span> <span class="type">void</span> setSpecies(<span class="directive">final</span> PetSpecies species) { <span class="local-variable">this</span>.species = species; } - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Update fixtures, unit tests and integration tests.</p> - </div> + <div class="ulist"> + <ul> + <li> <p>a todo app (<code>ToDoItem</code>s)</p> </li> + <li> <p>a pet clinic (<code>Pet</code>, <code>Owner</code>, <code>PetSpecies</code>, <code>Visit</code>)</p> </li> + <li> <p>a library (<code>Book</code>, <code>Title</code>, <code>LibraryMember</code>, <code>Loan</code>, <code>Reservation</code>)</p> </li> + <li> <p>a holiday cottage rental system</p> </li> + <li> <p>a scrum/kanban system (inspired by Trello)</p> </li> + <li> <p>a meeting planner (inspired by Doodle)</p> </li> + <li> <p>(the domain model for) a CI server (inspired by Travis/Jenkins)</p> </li> + <li> <p>a shipping system (inspired by the example in the DDD "blue" book)</p> </li> + <li> <p>a system for ordering coffee (inspired by Restbucks, the example in "Rest in Practice" book)</p> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_icon_to_reflect_pet_species">2.13. Icon to reflect pet species</h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/2212765694693eb463f8fa88bab1bad154add0cb</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>Rather than using a single icon for a domain class, instead a different icon can be supplied for each instance. We can therefore have different icon files for each pet, reflecting that petâs species.</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">Pet</span> { - ... - public <span class="predefined-type">String</span> iconName() { - <span class="keyword">return</span> getSpecies().name(); - } - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Download corresponding icon files (<code>Dog.png</code>, <code>Cat.png</code> etc)</p> - </div> - <div class="paragraph"> - <p>Running the app shows the <code>Pet</code> and its associated icon:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/050-01-list-all.png"><img src="images/tutorials/pet-clinic/050-01-list-all.png" alt="050 01 list all" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>with the corresponding view of the <code>Pet</code>:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/050-02-view-pet.png"><img src="images/tutorials/pet-clinic/050-02-view-pet.png" alt="050 02 view pet" width="600px"></a> - </div> - </div> + <div class="paragraph"> + <p>Hopefully one of those ideas appeals or sparks an idea for something of your own.</p> </div> - <div class="sect2"> - <h3 id="_add_pet_s_code_owner_code">2.14. Add petâs <code>Owner</code></h3> - <div class="admonitionblock tip"> - <table> - <tbody> - <tr> - <td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td> - <td class="content"> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">git checkout https://github.com/danhaywood/isis-app-petclinic/commit/6f92a8ee8e76696d005da2a8b7a746444d017546</code></pre> - </div> - </div> </td> - </tr> - </tbody> - </table> - </div> - <div class="paragraph"> - <p>Add the <code>Owner</code> entity and corresponding <code>Owners</code> domain service (repository). Add a query to find `Order`s by name:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java">... -<span class="annotation">@javax</span>.jdo.annotations.Queries( { - <span class="annotation">@javax</span>.jdo.annotations.Query( - name = <span class="string"><span class="delimiter">"</span><span class="content">findByName</span><span class="delimiter">"</span></span>, language = <span class="string"><span class="delimiter">"</span><span class="content">JDOQL</span><span class="delimiter">"</span></span>, - value = <span class="string"><span class="delimiter">"</span><span class="content">SELECT </span><span class="delimiter">"</span></span> - + <span class="string"><span class="delimiter">"</span><span class="content">FROM dom.owners.Owner </span><span class="delimiter">"</span></span> - + <span class="string"><span class="delimiter">"</span><span class="content">WHERE name.matches(:name)</span><span class="delimiter">"</span></span>) -}) -<span class="directive">public</span> <span class="type">class</span> <span class="class">Owner</span> ... { - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>and <code>findByName(â¦â)</code> in <code>Owners</code>:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">Owners</span> { - ... - public <span class="predefined-type">List</span><<span class="predefined-type">Owner</span>> findByName( - <span class="annotation">@ParameterLayout</span>(named = <span class="string"><span class="delimiter">"</span><span class="content">Name</span><span class="delimiter">"</span></span>) - <span class="directive">final</span> <span class="predefined-type">String</span> name) { - <span class="directive">final</span> <span class="predefined-type">String</span> nameArg = <span class="predefined-type">String</span>.format(<span class="string"><span class="delimiter">"</span><span class="content">.*%s.*</span><span class="delimiter">"</span></span>, name); - <span class="directive">final</span> <span class="predefined-type">List</span><<span class="predefined-type">Owner</span>> owners = container.allMatches( - <span class="keyword">new</span> QueryDefault<>( - <span class="predefined-type">Owner</span>.class, - <span class="string"><span class="delimiter">"</span><span class="content">findByName</span><span class="delimiter">"</span></span>, - <span class="string"><span class="delimiter">"</span><span class="content">name</span><span class="delimiter">"</span></span>, nameArg)); - <span class="keyword">return</span> owners; - } - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Add an <code>owner</code> property to <code>Pet</code>, with supporting <code>autoCompleteXxx()</code> method (so that available owners are shown in a drop-down list box):</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="java"><span class="directive">public</span> <span class="type">class</span> <span class="class">Pet</span> ... { - ... - private <span class="predefined-type">Owner</span> owner; - <span class="annotation">@javax</span>.jdo.annotations.Column(allowsNull = <span class="string"><span class="delimiter">"</span><span class="content">false</span><span class="delimiter">"</span></span>) - <span class="directive">public</span> <span class="predefined-type">Owner</span> getOwner() { <span class="keyword">return</span> owner; } - <span class="directive">public</span> <span class="type">void</span> setOwner(<span class="directive">final</span> <span class="predefined-type">Owner</span> owner) { <span class="local-variable">this</span>.owner = owner; } - <span class="directive">public</span> <span class="predefined-type">Collection</span><<span class="predefined-type">Owner</span>> autoCompleteOwner(<span class="directive">final</span> <span class="annotation">@MinLength</span>(<span class="integer">1</span>) <span class="predefined-type">String</span> name) { - <span class="keyword">return</span> owners.findByName(name); - } - ... -}</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Also updated fixture data to set up a number of <code>Owner`s, and associate each `Pet</code> with an <code>Owner</code>. Also add unit tests and integration tests for <code>Owner</code>/<code>Owners</code> and updated for <code>Pet</code>/<code>Pets</code>.</p> - </div> - <div class="paragraph"> - <p>When running the app, notice the new <code>Owners</code> menu:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/060-01-owners-menu.png"><img src="images/tutorials/pet-clinic/060-01-owners-menu.png" alt="060 01 owners menu" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>which when invoked returns all <code>Owner</code> objects:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/060-02-owners-list.png"><img src="images/tutorials/pet-clinic/060-02-owners-list.png" alt="060 02 owners list" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>Each <code>Pet</code> also indicates its corresponding <code>Owner</code>:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/060-03-pets-list.png"><img src="images/tutorials/pet-clinic/060-03-pets-list.png" alt="060 03 pets list" width="600px"></a> - </div> - </div> - <div class="paragraph"> - <p>And, on editing a <code>Pet</code>, a new <code>Owner</code> can be specified using the autoComplete:</p> - </div> - <div class="imageblock"> - <div class="content"> - <a class="image" href="images/tutorials/pet-clinic/060-04-pet-owner-autoComplete.png"><img src="images/tutorials/pet-clinic/060-04-pet-owner-autoComplete.png" alt="060 04 pet owner autoComplete" width="600px"></a> - </div> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_domain_entity">10. Domain entity</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Most domain objects in Apache Isis applications are persistent entities. In the simpleapp archetype the <code>SimpleObject</code> is an example. We can start developing our app by refactoring that class:</p> + </div> + <div class="ulist"> + <ul> + <li> <p>rename the <code>SimpleObject</code> class</p> + <div class="ulist"> + <ul> + <li> <p>eg rename to <code>Pet</code></p> </li> + </ul> + </div> </li> + <li> <p>if required, rename the <code>SimpleObject</code> class' <code>name</code> property</p> + <div class="ulist"> + <ul> + <li> <p>for <code>Pet</code>, can leave <code>name</code> property as is</p> </li> + </ul> + </div> </li> + <li> <p>specify a <a href="../../guides/ugfun/ugfun.html#_ugfun_ui-hints_object-titles-and-icons">title</a></p> </li> + <li> <p>specify an <a href="../../guides/ugfun/ugfun.html#_ugfun_ui-hints_object-titles-and-icons">icon</a></p> </li> + <li> <p>make the entity bookmarkable by adding the <a href="../../guides/rgant/rgant.html#_rgant-DomainObjectLayout_bookmarking"><code>@DomainObjectLayout#bookmarking()</code></a> attribute.</p> </li> + <li> <p>confirm is available from bookmark panel (top-left of Wicket UI)</p> </li> + </ul> </div> </div> </div> <div class="sect1"> - <h2 id="_tg_pet-clinic-extended">3. Pet Clinic (Extended)</h2> - <div class="btn-group" style="float: right; font-size: small; padding: 6px; margin-top: -55px; "> - <button type="button" class="btn btn-xs btn-default" onclick="window.location.href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic-extended.adoc""><i class="fa fa-pencil-square-o"></i> Edit</button> - <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span><span class="sr-only">Toggle Dropdown</span></button> - <ul class="dropdown-menu"> - <li><a href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic-extended.adoc" target="_blank"><i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> Edit</a></li> - <li><a href="https://github.com/apache/isis/commits/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic-extended.adoc" target="_blank"><i class="fa fa-clock-o fa-fw" aria-hidden="true"></i> History</a></li> - <li><a href="https://github.com/apache/isis/raw/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic-extended.adoc" target="_blank"><i class="fa fa-file-text-o fa-fw" aria-hidden="true"></i> Raw</a></li> - <li><a href="https://github.com/apache/isis/blame/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_pet-clinic-extended.adoc" target="_blank"><i class="fa fa-hand-o-right fa-fw" aria-hidden="true"></i> Blame</a></li> - </ul> + <h2 id="_domain_service">11. Domain service</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Domain services often act as factories or repositories to entities; more generally can be used to "bridge across" to other domains/bounded contexts. Most are application-scoped, but they can also be request-scoped if required.</p> + </div> + <div class="paragraph"> + <p>In the simpleapp archetype the <code>SimpleObjects</code> service is a factory/repository for the original <code>SimpleObject</code> entity. For our app it therefore makes sense to refactor that class into our own first service:</p> + </div> + <div class="ulist"> + <ul> + <li> <p>rename the <code>SimpleObjects</code> class</p> + <div class="ulist"> + <ul> + <li> <p>eg rename to <code>Pets</code></p> </li> + </ul> + </div> </li> + <li> <p>review <code>create</code> action (acting as a factory)</p> + <div class="ulist"> + <ul> + <li> <p>as per the docs describing <a href="../../guides/ugfun/ugfun.html#_ugfun_crud">how to create or delete objects</a></p> </li> + </ul> + </div> </li> + <li> <p>rename if you wish</p> + <div class="ulist"> + <ul> + <li> <p>eg <code>newPet(â¦â)</code> or <code>addPet(â¦â)</code></p> </li> + </ul> + </div> </li> + <li> <p>review <code>listAll</code> action (acting as a repository)</p> </li> + <li> <p>as per the docs describing <a href="../../guides/ugfun/ugfun.html#_ugfun_crud">how to write a custom repository</a></p> </li> + <li> <p>note the annotations on the corresponding domain class (originally called <code>SimpleObject</code>, though renamed by now, eg to <code>Pet</code>)</p> </li> + <li> <p>rename if you wish</p> + <div class="ulist"> + <ul> + <li> <p>eg <code>listPets()</code></p> </li> + </ul> + </div> </li> + <li> <p>note the <a href="../../guides/rgant/rgant.html#_rgant-DomainService"><code>@DomainService</code></a> annotation</p> </li> + <li> <p>optional: add an action to a return subset of objects</p> + <div class="ulist"> + <ul> + <li> <p>use the JDO <code>@Query</code> annotation</p> </li> + <li> <p>see for example the Isisaddons example <a href="https://github.com/isisaddons/isis-app-todoapp">todoapp</a> (not ASF), see <a href="https://github.com/apache/isis/blob/b3e936c9aae28754fb46c2df52b1cb9b023f9ab8/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java#L93">here</a> and <a href="https://github.com/apache/isis/blob/b3e936c9aae28754fb46c2df52b1cb9b023f9ab8/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItems.java#L63">here</a></p> </li> + </ul> + </div> </li> + </ul> + </div> </div> + </div> + <div class="sect1"> + <h2 id="_fixture_scripts">12. Fixture scripts</h2> <div class="sectionbody"> <div class="paragraph"> - <p>An extended version of the <a href="#_tg_tutorials_pet-clinic">pet clinic</a> can be found on <a href="https://github.com/johandoornenbal/tutorial_code/blob/master/AsciiDoc/1_petclinic_introduction.adoc">this github repo</a>. It was written by Johan Doornenbal.</p> + <p>Fixture scripts are used to setup the app into a known state. They are great for demoâs and as a time-saver when implementing a feature, and they can also be reused in automated integration tests. We usually also have a fixture script to zap all the (non-reference) data (or some logical subset of the data)</p> + </div> + <div class="ulist"> + <ul> + <li> <p>rename the <code>SimpleObjectsTearDownFixture</code> class</p> </li> + <li> <p>and update to delete from the appropriate underlying database table(s)</p> </li> + <li> <p>use the injected <a href="../../guides/rgsvc/rgsvc.html#_rgsvc_persistence-layer-api_IsisJdoSupport"><code>IsisJdoSupport</code></a> domain service.</p> </li> + <li> <p>refactor/rename the fixture script classes that create instances your entity:</p> </li> + <li> <p><code>RecreateSimpleObjects</code>, which sets up a set of objects for a given scenario</p> </li> + <li> <p><code>SimpleObjectCreate</code> which creates a single object</p> </li> + <li> <p>note that domain services can be injected into these fixture scripts</p> </li> + </ul> </div> + </div> + </div> + <div class="sect1"> + <h2 id="_actions">13. Actions</h2> + <div class="sectionbody"> <div class="paragraph"> - <p>This version also includes a <a href="https://github.com/johandoornenbal/petclinic_mynewcode">sample solution</a>, also as a github repo.</p> + <p>Most business functionality is implemented using actions basically a <code>public</code> method accepting domain classes and primitives as its parameter types. The action can return a domain entity, or a collection of entities, or a primitive/String/value, or void. If a domain entity is returned then that object is rendered immediately; if a collection is returned then the Wicket viewer renders a table. Such collections are sometimes called "standalone" collections.</p> + </div> + <div class="ulist"> + <ul> + <li> <p>write an action to update the domain property (originally called <code>SimpleObject#name</code>, though renamed by now)</p> </li> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-ParameterLayout_named"><code>@ParameterLayout(named=â¦â)</code></a> annotation to specify the name of action parameters</p> </li> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-Action_semantics"><code>@Action(semanticsOf=â¦â)</code></a> annotation to indicate the semantics of the action (safe/query-only, idempotent or non-idempotent)</p> </li> + <li> <p>annotate safe action as bookmarkable using <a href="../../guides/rgant/rgant.html#_rgant-ActionLayout_bookmarking"><code>@ActionLayout(bookmarking=â¦â)</code></a></p> </li> + <li> <p>confirm is available from bookmark panel (top-left of Wicket UI)</p> </li> + <li> <p>optional: add an action to clone an object</p> </li> + </ul> </div> </div> </div> <div class="sect1"> - <h2 id="_tg_stop-scaffolding-start-coding">4. Stop scaffolding, start coding</h2> - <div class="btn-group" style="float: right; font-size: small; padding: 6px; margin-top: -55px; "> - <button type="button" class="btn btn-xs btn-default" onclick="window.location.href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_stop-scaffolding-start-coding.adoc""><i class="fa fa-pencil-square-o"></i> Edit</button> - <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="caret"></span><span class="sr-only">Toggle Dropdown</span></button> - <ul class="dropdown-menu"> - <li><a href="https://github.com/apache/isis/edit/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_stop-scaffolding-start-coding.adoc" target="_blank"><i class="fa fa-pencil-square-o fa-fw" aria-hidden="true"></i> Edit</a></li> - <li><a href="https://github.com/apache/isis/commits/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_stop-scaffolding-start-coding.adoc" target="_blank"><i class="fa fa-clock-o fa-fw" aria-hidden="true"></i> History</a></li> - <li><a href="https://github.com/apache/isis/raw/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_stop-scaffolding-start-coding.adoc" target="_blank"><i class="fa fa-file-text-o fa-fw" aria-hidden="true"></i> Raw</a></li> - <li><a href="https://github.com/apache/isis/blame/master/adocs/documentation/src/main/asciidoc/pages/tg/_tg_stop-scaffolding-start-coding.adoc" target="_blank"><i class="fa fa-hand-o-right fa-fw" aria-hidden="true"></i> Blame</a></li> - </ul> + <h2 id="_rest_api">14. REST API</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>As well as exposing the Wicket viewer, Isis also exposes a REST API (an implementation of the <a href="http://restfulobjects.org">Restful Objects spec</a>). All of the functionality of the domain object model is available through this REST API.</p> + </div> + <div class="ulist"> + <ul> + <li> <p>add Chrome extensions</p> </li> + <li> <p>install <a href="https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en">Postman</a></p> </li> + <li> <p>install <a href="https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=en">JSON-View</a></p> </li> + <li> <p>browse to Wicket viewer, install fixtures</p> </li> + <li> <p>browse to the <a href="http://localhost:8080/restful">http://localhost:8080/restful</a> API</p> </li> + <li> <p>invoke the service to list all objects</p> </li> + <li> <p>services</p> </li> + <li> <p>actions</p> </li> + <li> <p>invoke (invoking 0-arg actions is easy; the Restful Objects spec defines how to invoke N-arg actions)</p> </li> + </ul> + </div> </div> + </div> + <div class="sect1"> + <h2 id="_specify_action_semantics">15. Specify Action semantics</h2> <div class="sectionbody"> <div class="paragraph"> - <p>This is a half-day tutorial on developing domain-driven apps using Apache Isis. Actually, you could probably spend a full day working through this tutorial if you wanted to, so pick and choose the bits that look interesting. It was originally written by Dan Haywood.</p> + <p>The semantics of an action (whether it is safe/query only, whether it is idempotent, whether it is neither) can be specified for each action; if not specified then Isis assumes non-idempotent. In the Wicket viewer this matters in that only query-only actions can be bookmarked or used as contributed properties/collections. In the RESTful viewer this matters in that it determines the HTTP verb (GET, PUT or POST) that is used to invoke the action.</p> + </div> + <div class="ulist"> + <ul> + <li> <p>experiment changing <a href="../../guides/rgant/rgant.html#_rgant-Action_semantics"><code>@Action(semantics=â¦â)</code></a> on actions</p> </li> + <li> <p>note the HTTP methods exposed in the REST API change</p> </li> + <li> <p>note whether the non-safe actions are bookmarkable (assuming that it has been annotated with <code>@ActionLayout(bookmarking=â¦â)</code>, that is).</p> </li> + </ul> </div> + </div> + </div> + <div class="sect1"> + <h2 id="_value_properties">16. Value properties</h2> + <div class="sectionbody"> <div class="paragraph"> - <p>Thereâs a bit of overlap with the <a href="#_tg_tutorials_pet-clinic">Pet Clinic</a> tutorial initially, but it then sets off on its own.</p> + <p>Domain entities have state: either values (primitives, strings) or references to other entities. In this section we explore adding some value properties</p> </div> - <div class="sect2"> - <h3 id="_prerequisites_2">4.1. Prerequisites</h3> - <div class="paragraph"> - <p>Youâll need:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>Java 7 JDK</p> </li> - <li> <p><a href="http://maven.apache.org/">Maven</a> 3.2.x</p> </li> - <li> <p>an IDE, such as <a href="http://www.eclipse.org/">Eclipse</a> or <a href="https://www.jetbrains.com/idea/">IntelliJ IDEA</a>.</p> </li> - </ul> - </div> + <div class="ulist"> + <ul> + <li> <p>add some <a href="../../guides/ugfun/ugfun.html#_ugfun_programming-model_properties">value properties</a>; also:</p> </li> + <li> <p>for string properties</p> + <div class="ulist"> + <ul> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-PropertyLayout_multiLine"><code>@PropertyLayout(multiLine=â¦â)</code></a> annotation to render a text area instead of a text box</p> </li> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-Property_maxLength"><code>@Property(maxLength=â¦â)</code></a> annotation to specify the maximum number of characters allowable</p> </li> + <li> <p>use joda date/time properties, bigdecimals and blob/clob properties</p> </li> + </ul> + </div> </li> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-Property_optionality"><code>@Column(allowsNull=â¦â)</code></a> annotation specify whether a property is optional or mandatory</p> </li> + <li> <p>use enums for properties (eg as used in the Isis addons example <a href="https://github.com/isisaddons/isis-app-todoapp">todoapp</a>, see <a href="https://github.com/apache/isis/blob/b3e936c9aae28754fb46c2df52b1cb9b023f9ab8/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java#L207">here</a> and <a href="https://github.com/apache/isis/blob/b3e936c9aae28754fb46c2df52b1cb9b023f9ab8/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java#L266">here</a>)</p> </li> + <li> <p>update the corresponding domain service for creating new instances</p> </li> + <li> <p>for all non-optional properties will either need to prompt for a value, or calculate some suitable default</p> </li> + <li> <p>change the implementation of title, if need be</p> </li> + <li> <p>revisit the title, consider whether to use the <a href="../../guides/rgant/rgant.html#_rgant-Title"><code>@Title</code></a> annotation</p> + <div class="ulist"> + <ul> + <li> <p>rather than the <a href="../../guides/rgcms/rgcms.html#_rgcms_methods_reserved_title"><code>title()</code></a> method</p> </li> + </ul> + </div> </li> + <li> <p>order the properties using the <a href="../../guides/rgant/rgant.html#_rgant-MemberOrder"><code>@MemberOrder</code></a>, also <code>@MemberGroupLayout</code></p> + <div class="ulist"> + <ul> + <li> <p>see also the docs on <a href="../../guides/ugvw/ugvw.html#_ugvw_layout_annotation-based">static layouts</a></p> </li> + </ul> + </div> </li> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-PropertyLayout"><code>@PropertyLayout</code></a> annotation to position property/action parameter labels either to the LEFT, TOP or NONE</p> + <div class="ulist"> + <ul> + <li> <p>do the same for parameters using <a href="../../guides/rgant/rgant.html#_rgant-ParameterLayout"><code>@ParameterLayout</code></a></p> </li> + </ul> + </div> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_run_the_archetype_2">4.2. Run the archetype</h3> - <div class="paragraph"> - <p>Run the simpleapp archetype to build an empty Isis application. With the *nix bash shell, use:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn archetype:generate \ - -D archetypeGroupId=org.apache.isis.archetype \ - -D archetypeArtifactId=simpleapp-archetype \ - -D archetypeVersion=1.16.0 \ - -D groupId=com.mycompany \ - -D artifactId=myapp \ - -D version=1.0-SNAPSHOT \ - -D archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \ - -B</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Adjust as necessary if using Windows <code>cmd.exe</code> or Powershell.</p> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_reference_properties">17. Reference properties</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Domain entities can also reference other domain entities. These references may be either scalar (single-valued) or vector (multi-valued). In this section we focus on scalar reference properties.</p> </div> - <div class="sect2"> - <h3 id="_build_and_run_2">4.3. Build and run</h3> - <div class="paragraph"> - <p>Start off by building the app from the command line:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">cd myapp -mvn clean install -D mavenmixin-jettyconsole</code></pre> - </div> - </div> - <div class="paragraph"> - <p>Once thatâs built then run using:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn -pl webapp antrun:run -D mavenmixin-jettyconsole</code></pre> - </div> - </div> - <div class="paragraph"> - <p>A splash screen should appear offering to start up the app. Go ahead and start; the web browser should be opened at <a href="http://localhost:8080">http://localhost:8080</a></p> - </div> - <div class="paragraph"> - <p>Alternatively, you can run using the mvn-jetty-plugin:</p> - </div> - <div class="listingblock"> - <div class="content"> - <pre class="CodeRay highlight"><code data-lang="bash">mvn -pl webapp jetty:run</code></pre> - </div> - </div> - <div class="paragraph"> - <p>This will accomplish the same thing, though the webapp is mounted at a slightly different URL</p> - </div> + <div class="ulist"> + <ul> + <li> <p>add some <a href="../../guides/ugfun/ugfun.html#_ugfun_programming-model_properties">reference properties</a></p> </li> + <li> <p>update the corresponding domain service (for creation actoin)</p> </li> + <li> <p>use different techniques to obtain references (shown in drop-down list box)</p> + <div class="ulist"> + <ul> + <li> <p>use the <a href="../../guides/rgant/rgant.html#_rgant-DomainObject_bounded"><code>@DomainObjectLayout(bounded=â¦â)</code></a> annotation on the referenced type if there are only a small number (bounded) of instances</p> </li> + <li> <p>use a <a href="../../guides/rgcms/rgcms.html#_rgcms_methods_prefixes_choices"><code>choicesâ¦â()</code></a> supporting method</p> + <div class="ulist"> + <ul> + <li> <p>on a property</p> </li> + <li> <p>on an action parameter</p> </li> + </ul> + </div> </li> + <li> <p>use a <a href="../../guides/rgcms/rgcms.html#_rgcms_methods_prefixes_autoComplete"><code>autoCompleteâ¦â()</code></a> supporting method</p> + <div class="ulist"> + <ul> + <li> <p>on a property</p> </li> + <li> <p>on an action parameter</p> </li> + </ul> + </div> </li> + </ul> + </div> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_using_the_app_2">4.4. Using the app</h3> - <div class="paragraph"> - <p>Navigate to the Wicket UI (eg <a href="http://localhost:8080/wicket">http://localhost:8080/wicket</a>), and login (sven/pass).</p> - </div> - <div class="paragraph"> - <p>Once at the home page:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>install fixtures</p> </li> - <li> <p>list all objects</p> </li> - <li> <p>create a new object</p> </li> - <li> <p>list all objects</p> </li> - </ul> - </div> - <div class="paragraph"> - <p>Go back to the splash screen, and quit the app. Note that the database runs in-memory (using HSQLDB) so any data created will be lost between runs.</p> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_usability_defaults">18. Usability: Defaults</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Quick detour: often we want to set up defaults to go with choices. Sensible defaults for action parameters can really improve the usability of the app.</p> </div> - <div class="sect2"> - <h3 id="_dev_environment_2">4.5. Dev environment</h3> - <div class="paragraph"> - <p>Set up <a href="../../guides/dg/dg.html#_dg_ide">an IDE</a> and import the project to be able to run and debug the app.</p> - </div> - <div class="paragraph"> - <p>Then set up a launch configuration and check that you can:</p> - </div> - <div class="ulist"> - <ul> - <li> <p>Run the app from within the IDE</p> </li> - <li> <p>Run the app in debug mode</p> </li> - <li> <p>Run with different deploymentTypes; note whether prototype actions (those annotated <a href="../../guides/rgant/rgant.html#_rgant-Action_restrictTo"><code>@Action(restrictTo=PROTOTYPING</code></a>) are available or not:</p> </li> - <li> <p><code>--type SERVER_PROTOTYPE</code></p> </li> - <li> <p><code>--type SERVER</code></p> </li> - </ul> - </div> + <div class="ulist"> + <ul> + <li> <p>Add <a href="../../guides/ugfun/ugfun.html#_ugfun_drop-downs-and-defaults">defaults</a> for action parameters</p> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_explore_codebase_2">4.6. Explore codebase</h3> - <div class="paragraph"> - <p>Apache Isis applications are organized into several Maven modules. Within your IDE navigate to the various classes and correlate back to the generated UI:</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>myapp</code> : parent module</p> </li> - <li> <p><code>myapp-dom</code>: domain objects module</p> </li> - <li> <p>entity: <code>dom.simple.SimpleObject</code></p> </li> - <li> <p>repository: <code>dom.simple.SimpleObjects</code></p> </li> - <li> <p><code>myapp-fixture</code>: fixtures module</p> </li> - <li> <p>fixture script:`fixture.simple.SimpleObjectsFixture`</p> </li> - <li> <p><code>myapp-integtests</code>: integration tests module</p> </li> - <li> <p><code>myapp-webapp</code>: webapp module</p> </li> - <li> <p>(builds the WAR file)</p> </li> - </ul> - </div> + </div> + </div> + <div class="sect1"> + <h2 id="_collections">19. Collections</h2> + <div class="sectionbody"> + <div class="paragraph"> + <p>Returning back to references, Isis also supports vector (multi-valued) references to another object instances in other words collections. We sometimes called these "parented" collections (to distinguish from a "standalone" collection as returned from an action)</p> </div> - <div class="sect2"> - <h3 id="_testing_2">4.7. Testing</h3> - <div class="paragraph"> - <p>Testing is of course massively important, and Apache Isis makes both unit testing and (end-to-end) integration testing easy. Building the app from the Maven command line ("mvn clean install") will run all tests, but you should also run the tests from within the IDE.</p> - </div> - <div class="ulist"> - <ul> - <li> <p><code>myapp-dom</code> unit tests</p> </li> - <li> <p>run</p> </li> - <li> <p>inspect, eg</p> - <div class="ulist"> - <ul> - <li> <p><code>SimpleObjectTest</code></p> </li> - </ul> - </div> </li> - <li> <p><code>myapp-integtests</code> integration tests</p> </li> - <li> <p>run</p> </li> - <li> <p>inspect, eg:</p> - <div class="ulist"> - <ul> - <li> <p><code>integration.tests.smoke.SimpleObjectsTest</code></p> </li> - <li> <p><code>integration.specs.simple.SimpleObjectSpec_listAllAndCreate.feature</code></p> </li> - </ul> - </div> </li> - <li> <p>generated report, eg</p> - <div class="ulist"> - <ul> - <li> <p><code>myapp/integtests/target/cucumber-html-report/index.html</code></p> - <div class="ulist"> - <ul> - <li> <p>change test in IDE, re-run (in Maven)</p> </li> - </ul> - </div> </li> - </ul> - </div> </li> - </ul> - </div> - <div class="paragraph"> - <p>If you have issues with the integration tests, make sure that the domain classes have been enhanced by the DataNucleus enhancer. (The exact mechanics depends on the IDE being used).</p> - </div> + <div class="ulist"> + <ul> + <li> <p>Ensure that all domain classes implement <code>java.lang.Comparable</code></p> + <div class="ulist"> + <ul> + <li> <p>use the <a href="../../guides/rgcms/rgcms.html#_rgcms_classes_utility_ObjectContracts"><code>ObjectContracts</code></a> utility class to help implement <code>Comparable</code></p> + <div class="ulist"> + <ul> + <li> <p>you can also implement <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code></p> </li> + </ul> + </div> </li> + </ul> + </div> </li> + <li> <p>Add a <a href="../../guides/ugfun/ugfun.html#_ugfun_programming-model_collections">collection</a> to one of the entities</p> + <div class="ulist"> + <ul> + <li> <p>Use <code>SortedSet</code> as the class</p> </li> + <li> <p>Use the <a href="../../guides/rgant/rgant.html#_rgant-CollectionLayout_render"><code>@CollectionLayout(render=â¦â)</code></a> annotation to indicate if the collection should be visible or hidden by default</p> </li> + </ul> + </div> </li> + <li> <p>optional: use the <a href="../../guides/rgant/rgant.html#_rgant-CollectionLayout_sortedBy"><code>@CollectionLayout(sortedBy=â¦â)</code></a> annotation to specify a different comparator than the natural ordering</p> </li> + </ul> </div> - <div class="sect2"> - <h3 id="_prototyping">4.8. Prototyping</h3> - <div class="paragraph"> - <p>Although testing is important, in this tutorial we want to concentrate on how to write features and to iterate quickly. So for now, exclude the <code>integtests</code> module. Later on in the tutorial weâll add the tests back in so you can learn how to write automated tests for the features of your app.</p> - </div> - <div class="paragraph"> - <p>In t
<TRUNCATED>