You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by bu...@apache.org on 2014/11/18 21:55:24 UTC

svn commit: r929689 - in /websites/staging/isis/trunk: cgi-bin/ content/ content/documentation.html content/tutorials/ content/tutorials/apacheconeu-2014.html

Author: buildbot
Date: Tue Nov 18 20:55:23 2014
New Revision: 929689

Log:
Staging update by buildbot for isis

Added:
    websites/staging/isis/trunk/content/tutorials/
    websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
Modified:
    websites/staging/isis/trunk/cgi-bin/   (props changed)
    websites/staging/isis/trunk/content/   (props changed)
    websites/staging/isis/trunk/content/documentation.html

Propchange: websites/staging/isis/trunk/cgi-bin/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 20:55:23 2014
@@ -1 +1 @@
-1640372
+1640424

Propchange: websites/staging/isis/trunk/content/
------------------------------------------------------------------------------
--- cms:source-revision (original)
+++ cms:source-revision Tue Nov 18 20:55:23 2014
@@ -1 +1 @@
-1640372
+1640424

Modified: websites/staging/isis/trunk/content/documentation.html
==============================================================================
--- websites/staging/isis/trunk/content/documentation.html (original)
+++ websites/staging/isis/trunk/content/documentation.html Tue Nov 18 20:55:23 2014
@@ -741,11 +741,11 @@
 <ul>
 <li><a href="core/unittestsupport.html">Unit Test Support</a></li>
 <li><a href="core/integtestsupport.html">Integration Test Support</a></li>
-<li><a href="core/specsupport-and-integtestsupport.html">BDD/Integ Test Support</a></li>
-<li><a href="./more-advanced-topics/Fixture-Scripts.html">Fixture Scripts</a>
+<li><a href="core/specsupport-and-integtestsupport.html">BDD/Integ Test Support</a>
 <!--</li>
-<li><a href="./more-advanced-topics/03-Fixtures-and-SwitchUser.html">Fixtures</a> (out of date)
+<li><a href="./more-advanced-topics/Fixture-Scripts.html">Fixture Scripts</a>
 --></li>
+<li><a href="./more-advanced-topics/03-Fixtures-and-SwitchUser.html">Fixtures</a> (out of date)</li>
 <li><a href="components/objectstores/jdo/IsisConfigurationForJdoIntegTests.html">IsisConfigurationForJdoIntegTests</a></li>
 </ul>
 

Added: websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html
==============================================================================
--- websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html (added)
+++ websites/staging/isis/trunk/content/tutorials/apacheconeu-2014.html Tue Nov 18 20:55:23 2014
@@ -0,0 +1,2529 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+
+    <meta charset="utf-8">
+      <title></title>
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
+    <!--[if lt IE 9]>
+      <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+
+    
+    <!-- Le styles -->
+    <link href="./../bootstrap-3.0.0/css/bootstrap.css" rel="stylesheet">
+    <link href="./../prettify.css" rel="stylesheet">
+
+    <style type="text/css">
+        body {
+          padding-top: 60px;
+        }
+        .sprite {
+            display: inline-block;
+            height: 20px;
+            margin: 0 auto 4px;
+            outline: medium none;
+            text-indent: -999em;
+            width: 24px;
+            background-image: url('./../images/sprites.png');
+            background-repeat: no-repeat;
+            overflow: hidden;
+            cursor: pointer;
+        }
+        .edit-page {
+            display: inline-block;
+            height: 20px;
+            margin: 0 auto 4px;
+            outline: medium none;
+            text-indent: -999em;
+            width: 24px;
+            background-image: url('./../images/edit.png');
+            background-repeat: no-repeat;
+            overflow: hidden;
+            cursor: pointer;
+        }
+        .fb-share {
+            background-position: 0px -40px;
+        }
+        .gp-share {
+            background-position: 0px 0px;
+        }
+        .tw-share {
+            background-position: 0px -80px;
+        }
+        .markdown-content {
+            min-height: 500px;
+        }
+        .book-image img {
+          border: 1px;
+          border-style: solid;
+        }
+        .release-matrix .heading {
+            background-color: #eeeeee;
+        }
+        .release-matrix .new {
+            color: #dd0000;
+            font-weight: bolder;
+        }
+        .stub,.note {
+            position: relative;
+            padding: 7px 15px;
+            margin-bottom: 18px;
+            color: #404040;
+            background-color: #eedc94;
+            background-repeat: repeat-x;
+            background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));
+            background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));
+            background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
+            background-image: -o-linear-gradient(top, #fceec1, #eedc94);
+            background-image: linear-gradient(top, #fceec1, #eedc94);
+            filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);
+            text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+            border-color: #eedc94 #eedc94 #e4c652;
+            border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+            text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+            border-width: 1px;
+            border-style: solid;
+            -webkit-border-radius: 4px;
+            -moz-border-radius: 4px;
+            border-radius: 4px;
+            -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+            -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+            box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
+        }
+        
+        div.XXXisis-tweak form {
+            margin-top: 6px;
+            margin-bottom: -2px;
+        }
+        
+        div.row div.col-md-12 {
+          border-top: 1px solid #eeeeee;
+       }
+        
+        
+        /* Isis specific stuff */
+
+        div.row div.col-md-12 {
+          margin-top: 20px;
+        }
+
+        .container {
+          width: 940px;
+        }
+
+        .markdown-content .documentation .span-one-third p {
+          margin-bottom: 0px;
+        }
+
+        .container .markdown-content .group,
+        .markdown-content .documentation .group {
+          margin-top: 9px;
+        }
+
+        .container .group h2,
+        .documentation .group h2 {
+          border-bottom: 1px solid #DDD
+        }
+
+        .container h2 a[name],
+        .documentation h2 a[name] {
+            padding-top: 50px;
+            margin-top: -50px;
+        }
+        
+        .container h2 a[name],
+        .container h3 a[name],
+        .container h4 a[name],
+        .documentation h2 a[name],
+        .documentation h3 a[name],
+        .documentation h4 a[name] {
+            color: black;
+            display: inline-block; 
+        }
+        .container h2 a[name]:hover,
+        .container h3 a[name]:hover,
+        .container h4 a[name]:hover,
+        .documentation h2 a[name]:hover,
+        .documentation h3 a[name]:hover,
+        .documentation h4 a[name]:hover {
+            text-decoration: none;
+        }
+
+        .documentation h2 a:not([name]),
+        .documentation h3 a:not([name]),
+        .documentation h4 a:not([name]) {
+          /* same as code style */
+          padding: 0 3px 2px;
+          font-family: Monaco, Andale Mono, Courier New, monospace;
+          font-size: 12px;
+          -webkit-border-radius: 3px;
+          -moz-border-radius: 3px;
+          border-radius: 3px;
+          padding: 1px 3px;
+        }
+        
+        .carousel-indicators li {
+          border: 1px solid rgb(192,70,1);
+        }
+
+        .carousel-indicators li.active {
+          background-color: rgb(192,70,1);
+        }
+        
+        .carousel-control .icon-prev,
+        .carousel-control .icon-next {
+          color: rgb(192,70,1);
+          font-size: 60px;
+        }
+        
+        .page-header {
+          margin-top: 0px;
+        }
+
+        .container blockquote p {
+            font-size: small;
+        }
+
+        .container blockquote p:not([author]) {
+            font-style: italic;
+        }
+
+        .container blockquote p {
+            font-size: small;
+            font-style: italic;
+            font-weight: bold;
+        }
+        
+        footer hr {
+            margin-top: 100px;
+        }
+
+        .markdown-content img {
+            margin-top: 10px;
+            margin-bottom: 20px;
+        }
+
+        .markdown-content a img {
+            margin-top: 0px;
+            margin-bottom: 0px;
+        }
+
+        
+        #forkongithub a{
+            display: none;
+            background:#090;
+            color:#fff;
+            text-decoration:none;
+            font-family:arial, sans-serif;
+            text-align:center;
+            font-weight:bold;
+            padding:5px 40px;
+            font-size:1rem;
+            line-height:2rem;
+            position:relative;
+            transition:0.5s;
+        }
+        #forkongithub a:hover{
+            background:#0D0;
+            color:#fff;
+        }
+        #forkongithub a::before,
+        #forkongithub a::after{
+            content:"";width:100%;
+            display:block;
+            position:fixed;
+            top:1px;
+            left:0;
+            height:1px;
+            background:#fff;
+            z-index: 9999;
+        }
+        #forkongithub a::after{
+            bottom:1px;
+            top:auto;
+        }
+        @media screen and (min-width:768px){
+            #forkongithub{
+                position:fixed;
+                display:block;
+                top:0;
+                right:0;
+                width:250px;
+                overflow:hidden;
+                height:250px;
+                z-index: 9999;
+            }
+            #forkongithub a{
+                display:inherit;
+                width:250px;
+                position:fixed;
+                font-size:small;
+                top:40px;
+                right:-60px;
+                transform:rotate(45deg);
+                -webkit-transform:rotate(45deg);
+                -ms-transform:rotate(45deg);
+                box-shadow:4px 4px 10px rgba(0,0,0,0.8);
+            }
+        }        
+    </style>
+
+    <!-- courtesy of http://codepo8.github.io/css-fork-on-github-ribbon/ -->
+    <span id="forkongithub">
+        <a href="https://github.com/apache/isis">Fork me on GitHub</a>
+    </span>
+
+    <script type="text/javascript">
+      function fbshare () {
+          window.open(
+                  "http://www.facebook.com/sharer/sharer.php?u="+document.URL,
+                  'Share on Facebook',
+                  'width=640,height=426');
+      };
+      function gpshare () {
+          window.open(
+                  "https://plus.google.com/share?url="+document.URL,
+                  'Share on Google+',
+                  'width=584,height=385');
+      };
+      function twshare () {
+          window.open(
+                  "https://twitter.com/intent/tweet?url="+document.URL+"&text=",
+                  'Share on Twitter',
+                  'width=800,height=526');
+      };
+    </script>
+
+    <!-- Le fav and touch icons -->
+    <link rel="shortcut icon" href="./../images/favicon.ico">
+
+    <script src="./../javascript/prettify.js" type="text/javascript"></script>
+    
+    <script src="//code.jquery.com/jquery.js"></script>
+    <script src="./../javascript/prettyprint.js"></script>
+    <script src="./../bootstrap-3.0.0/js/bootstrap.js"></script>
+
+
+    <script src="http://platform.twitter.com/widgets.js" type="text/javascript"></script>
+    <!--
+    <DISABLEDscript src="./../javascript/common.js"></DISABLEDscript>
+    -->
+
+    
+    
+    
+
+    <script>
+    $(function () { prettyPrint() })
+    $().dropdown()
+    </script>
+
+    
+  </head>
+
+  <body>
+
+    <nav class="navbar navbar-fixed-top navbar-inverse" role="navigation">
+      <div class="container">
+        <a class="navbar-brand" href="./../index.html">Apache Isis&trade;</a>
+        <ul class="nav navbar-nav">
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Demos<b class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a href="./../intro/elevator-pitch/isis-in-pictures.html">Screenshots</a></li>
+              <li><a href="./../intro/tutorials/screencasts.html">Screencasts<img src="./../images/tv_show-25.png"></a></li>
+              <li><a href="./../intro/powered-by/powered-by.html">Powered by</a></li>
+              <li><a href="http://isisdemo.mmyco.co.uk/" target="_blank">Online Demo</a></li>
+            </ul>
+          </li>
+
+          <li><a href="./../documentation.html">Docs</a></li>
+          <li><a href="http://www.isisaddons.org" target="_blank">Add-ons</a></li>
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Downloads<b class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a href="./../intro/getting-started/simpleapp-archetype.html">Isis (Maven archetype)</a></li>
+              <li><a href="./../download.html">Isis (downloads)</a></li>
+              <li><a href="http://www.isisaddons.org" target="_blank">Isis Add-ons</a></li>
+            </ul>
+          </li>
+
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Help<b class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li><a href="./../support.html">Mailing Lists</a></li>
+              <li><a href="http://isis.markmail.org/search/?q=" target="_blank">ML Archives</a></li>
+              <li><a href="https://issues.apache.org/jira/browse/ISIS" target="_blank">JIRA</a></li>
+              <li><a href="http://stackoverflow.com/questions/tagged/isis" target="_blank">Stack Overflow</a></li>
+              <li><a href="http://github.com/apache/isis" target="_blank">Github mirror</a></li>
+            </ul>
+          </li>
+
+          <li class="dropdown navbar-right">
+            <a href="#" class="dropdown-toggle" data-toggle="dropdown">@ASF<b class="caret"></b></a>
+            <ul class="dropdown-menu">
+              <li>
+                <a href="http://www.apache.org/" target="_blank">Apache Homepage <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/licenses/" target="_blank">Licenses <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/security/" target="_blank">Security <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/foundation/sponsorship.html" target="_blank">Sponsorship <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="http://www.apache.org/foundation/thanks.html">Thanks <i class="icon-share-alt"></i></a>
+              </li>
+              <li>
+                <a href="./../more-thanks.html">More thanks <i class="icon-share-alt"></i></a>
+              </li>
+            </ul>
+          </li>
+      </ul>
+
+        <FORM class="navbar-form navbar-right" id="searchbox_012614087480249044419:dn-q5gtwxya" action="http://www.google.com/cse">
+        <div class="form-group">
+          <input type="hidden" name="cx" value="012614087480249044419:dn-q5gtwxya">
+          <INPUT type="hidden" name="cof" value="FORID:0">
+          <INPUT class="form-control" name="q" type="text" placeholder="Search">
+        </div>
+      </FORM>
+
+
+      </div>
+
+
+  </nav>
+
+    <div class="container">
+      <div class="markdown-content">
+      
+
+<div class="page-header">
+<p><a href="./../documentation.html">Docs</a>&nbsp;&raquo&nbsp;<a href="./../tutorials/about.html">Tutorials</a></p>
+<h1>
+
+</h1>
+</div>
+
+<h1>Apache Isis: Stop scaffolding, start coding</h1>
+
+<h2>Run the archetype</h2>
+
+<pre><code>mvn archetype:generate  \
+    -D archetypeGroupId=org.apache.isis.archetype \
+    -D archetypeArtifactId=simpleapp-archetype \
+    -D archetypeVersion=1.8.0-SNAPSHOT \
+    -D groupId=com.mycompany \
+    -D artifactId=myapp \
+    -D version=1.0-SNAPSHOT \
+    -D archetypeRepository=http://repository-estatio.forge.cloudbees.com/snapshot/ \
+    -B
+</code></pre>
+
+<h2>Build and run</h2>
+
+<pre><code>cd myapp
+mvn clean install
+</code></pre>
+
+<p>then</p>
+
+<pre><code>mvn antrun:run -P self-host
+</code></pre>
+
+<p>or alternatively</p>
+
+<pre><code>mvn jetty:run    
+</code></pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>install fixtures</li>
+<li>list all</li>
+<li>create new</li>
+<li>list all    </li>
+</ul>
+
+<h2>Dev environment</h2>
+
+<h4>Configure</h4>
+
+<ul>
+<li>IDE:
+<ul>
+<li><a href="http://isis.apache.org/intro/getting-started/ide/intellij.html">IntelliJ</a></li>
+<li><a href="http://isis.apache.org/intro/getting-started/ide/eclipse.html">Eclipse</a></li>
+</ul></li>
+<li><a href="http://isis.apache.org/intro/resources/editor-templates.html">IDE Editor templates</a></li>
+</ul>
+
+<h4>Run</h4>
+
+<ul>
+<li>Run the app from within the IDE</li>
+<li>Run with different deploymentTypes:
+<ul>
+<li><code>--type SERVER_PROTOTYPE</code></li>
+<li><code>--type SERVER</code></li>
+</ul></li>
+</ul>
+
+<h2>Explore codebase</h2>
+
+<ul>
+<li><code>myapp</code> : parent module</li>
+<li><code>myapp-dom</code>
+<ul>
+<li><code>dom.simple.SimpleObject</code></li>
+<li><code>dom.simple.SimpleObjects</code></li>
+</ul></li>
+<li><code>myapp-fixture</code>
+<ul>
+<li><code>fixture.simple.SimpleObjectsFixture</code></li>
+</ul></li>
+<li><code>myapp-integtests</code></li>
+<li><code>myapp-webapp</code></li>
+</ul>
+
+<h2>Testing</h2>
+
+<ul>
+<li><code>myapp-dom</code> unit tests
+<ul>
+<li>run </li>
+<li>inspect, eg
+<ul>
+<li><code>SimpleObjectTest</code></li>
+</ul></li>
+</ul></li>
+<li><code>myapp-integtests</code> integration tests
+<ul>
+<li>run</li>
+<li>inspect, eg: 
+<ul>
+<li><code>integration.tests.smoke.SimpleObjectsTest</code></li>
+<li><code>integration.specs.simple.SimpleObjectSpec_listAllAndCreate.feature</code></li>
+</ul></li>
+<li>generated report, eg
+<ul>
+<li><code>myapp/integtests/target/cucumber-html-report/index.html</code></li>
+</ul></li>
+<li>change test in IDE, re-run (in Maven)   </li>
+</ul></li>
+</ul>
+
+<h2>Prototyping</h2>
+
+<p>Exclude the <code>integtests</code> module.</p>
+
+<p>In the parent <code>pom.xml</code>:</p>
+
+<pre><code>&lt;modules&gt;
+    &lt;module&gt;dom&lt;/module&gt;
+    &lt;module&gt;fixture&lt;/module&gt;
+    &lt;module&gt;integtests&lt;/module&gt;
+    &lt;module&gt;webapp&lt;/module&gt;
+&lt;/modules&gt;
+</code></pre>
+
+<p>change to:</p>
+
+<pre><code>&lt;modules&gt;
+    &lt;module&gt;dom&lt;/module&gt;
+    &lt;module&gt;fixture&lt;/module&gt;
+    &lt;!--
+    &lt;module&gt;integtests&lt;/module&gt;
+    --&gt;
+    &lt;module&gt;webapp&lt;/module&gt;
+&lt;/modules&gt;
+</code></pre>
+
+<h1>Build a domain app</h1>
+
+<p>The remainder of the tutorial provides guidance on building a domain application.  We'd rather you build your own app, but if you're not feeling inspired, you could have a go at building our petclinic app.  Here's the design:</p>
+
+<p><img src="http://yuml.me/a070d071" alt="" /></p>
+
+<pre>
+[Visit|-checkIn:DateTime;-checkout:DateTime;-diagnosis:String|+checkin();+checkout();+addNote()]->[Pet|-name:String;-species:PetSpecies]
+[Owner|-firstName:String;-lastName:String]<0..1-0..*>[Pet]
+</pre>
+
+<h2>Domain class</h2>
+
+<ul>
+<li>rename the <code>SimpleObject</code> class</li>
+<li>rename the <code>name</code> property</li>
+<li>specify a <a href="http://isis.apache.org/how-tos/how-to-01-040-How-to-specify-a-title-for-a-domain-entity.html">title</a></li>
+<li>specify an <a href="http://isis.apache.org/how-tos/how-to-01-070-How-to-specify-the-icon-for-a-domain-entity.html">icon</a></li>
+</ul>
+
+<h2>Domain service</h2>
+
+<ul>
+<li>rename the <code>SimpleObjects</code> class</li>
+<li>review <code>create</code> action (acting as a factory)
+<ul>
+<li>as per our <a href="http://isis.apache.org/how-tos/how-to-01-160-How-to-create-or-delete-objects-within-your-code.html">docs</a></li>
+</ul></li>
+<li>review <code>listAll</code> action (acting as a repository)
+<ul>
+<li>as per our <a href="http://isis.apache.org/how-tos/how-to-09-040-How-to-write-a-custom-repository.html">docs</a></li>
+<li>note the annotations on the corresponding domain class (<code>SimpleObject</code> above, probably renamed by now)</li>
+</ul></li>
+</ul>
+
+<h2>Fixture scripts</h2>
+
+<ul>
+<li>rename the <code>SimpleObjectsTearDownFixture</code> class
+<ul>
+<li>and update</li>
+</ul></li>
+<li>create for domain classes
+<ul>
+<li>inject in the corresponding domain service</li>
+</ul></li>
+</ul>
+
+<h2>Actions</h2>
+
+<ul>
+<li>update the domain property (<code>SimpleObject#name</code> above, renamed by now)</li>
+<li>use <a href="http://isis.apache.org/reference/recognized-annotations/ActionSemantics.html">@ActionSemantics</a> annotation to indicate the semantics of the action (safe/query-only, idempotent or non-idempotent)</li>
+<li>annotate safe action as <a href="http://isis.apache.org/reference/recognized-annotations/Bookmarkable.html">@Bookmarkable</a> 
+<ul>
+<li>confirm is available from bookmark panel (top-left of Wicket UI)</li>
+</ul></li>
+</ul>
+
+<h2>REST API</h2>
+
+<ul>
+<li>add Chrome extensions
+<ul>
+<li>install Postman</li>
+<li>install JSON-View</li>
+</ul></li>
+<li>browse to Wicket viewer, install fixtures</li>
+<li>browse to the http://localhost:8080/restful API</li>
+<li>invoke the service to list all objects</li>
+</ul>
+
+<h2>Specify Action semantics</h2>
+
+<ul>
+<li>note the HTTP methods exposed in the REST API</li>
+</ul>
+
+<h2>Value properties</h2>
+
+<ul>
+<li>add some <a href="http://isis.apache.org/how-tos/how-to-01-030-How-to-add-a-property-to-a-domain-entity.html">value properties</a>; also:
+<ul>
+<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-mandatory-and-optional-properties.html">optional vs mandatory</a></li>
+<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-joda-dates.html">joda date/time</a></li>
+<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-bigdecimals.html">bigdecimals</a></li>
+<li><a href="http://isis.apache.org/components/objectstores/jdo/mapping-blobs.html">blob/clobs</a></li>
+</ul></li>
+<li>update the corresponding domain service
+<ul>
+<li>if not optional</li>
+</ul></li>
+<li>update the title, if need be</li>
+<li><a href="http://isis.apache.org/how-tos/how-to-01-080-How-to-specify-the-order-in-which-properties-or-collections-are-displayed.html">order the properties</a> using the <a href="http://isis.apache.org/reference/recognized-annotations/MemberOrder.html">@MemberOrder</a> annotation and <a href="http://isis.apache.org/reference/recognized-annotations/MemberGroupLayout.html">@MemberGroupLayout</a> annotation
+<ul>
+<li>see also this <a href="http://isis.apache.org/components/viewers/wicket/static-layouts.html">static layouts</a> documentation</li>
+</ul></li>
+</ul>
+
+<h2>Reference properties</h2>
+
+<ul>
+<li>add some <a href="http://isis.apache.org/how-tos/how-to-01-030-How-to-add-a-property-to-a-domain-entity.html">reference properties</a></li>
+<li>update the corresponding domain service</li>
+<li>use different techniques to obtain references (shown in drop-down list box)
+<ul>
+<li>use <a href="http://isis.apache.org/reference/recognized-annotations/Bounded.html">@Bounded</a> annotation</li>
+<li>use the <a href="http://isis.apache.org/reference/recognized-annotations/AutoComplete.html">@AutoComplete</a> annotation</li>
+<li>use a <code>choicesXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-03-010-How-to-specify-a-set-of-choices-for-a-property.html">property</a> or <a href="http://isis.apache.org/how-tos/how-to-03-020-How-to-specify-a-set-of-choices-for-an-action-parameter.html">action param</a></li>
+<li>use an <code>autoCompleteXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-03-015-How-to-specify-an-autocomplete-for-a-property.html">property</a> or <a href="http://isis.apache.org/how-tos/how-to-03-025-How-to-specify-an-autocomplete-for-an-action-parameter.html">action param</a></li>
+</ul></li>
+</ul>
+
+<h2>Usability: Defaults</h2>
+
+<ul>
+<li>Add <a href="http://isis.apache.org/how-tos/how-to-03-050-How-to-specify-default-values-for-an-action-parameter.html">defaults</a> for action parameters</li>
+</ul>
+
+<h2>Collections</h2>
+
+<ul>
+<li>Ensure that all domain classes implement <code>Comparable</code>
+<ul>
+<li>use the <a href="http://isis.apache.org/reference/Utility.html">ObjectContracts</a> utility class to help implement <code>Comparable</code> (also <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code>)</li>
+</ul></li>
+<li>Add a <a href="http://isis.apache.org/components/objectstores/jdo/managed-1-to-m-relationships.html">one-to-many-collection</a> to one of the entities
+<ul>
+<li>Use <code>SortedSet</code> as the class</li>
+</ul></li>
+<li>TODO: @Render (http://isis.apache.org/reference/recognized-annotations/Render.html)</li>
+<li>optional: Use the <a href="http://isis.apache.org/reference/recognized-annotations/SortedBy.html">@SortedBy</a> annotation to specify a different comparator than the natural ordering</li>
+</ul>
+
+<h2>Actions (ctd)</h2>
+
+<ul>
+<li>Add domain actions to add/remove from the collection</li>
+</ul>
+
+<h2>Layout</h2>
+
+<ul>
+<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/MemberOrder.html">@MemberOrder</a> annotation to associate an action with a property or with a collection
+<ul>
+<li>set the <code>name</code> attribute</li>
+</ul></li>
+<li>Delete the <code>@MemberOrder</code> annotations and use the associated <a href="http://isis.apache.org/components/viewers/wicket/dynamic-layouts.html">.layout.json</a> file to specify layout hints instead</li>
+</ul>
+
+<h2>Business rules: See it!</h2>
+
+<ul>
+<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/Hidden.html">@Hidden</a> annotation to make properties/collections/actions invisible
+<ul>
+<li>the <a href="http://isis.apache.org/reference/recognized-annotations/Programmatic.html">@Programmatic</a> annotation can also be used and in many cases is to be preferred; the difference is that the latter means the member is not part of the Isis metamodel.</li>
+</ul></li>
+<li>Use the <code>hideXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-02-010-How-to-hide-a-property.html">properties</a>, <a href="http://isis.apache.org/how-tos/how-to-02-020-How-to-hide-a-collection.html">collections</a> and <a href="http://isis.apache.org/how-tos/how-to-02-030-How-to-hide-an-action.html">actions</a> to make a property/collection/action invisible according to some imperative rule</li>
+</ul>
+
+<h2>Business rules: Use it!</h2>
+
+<ul>
+<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/Disabled.html">@Disabled</a> annotation to make properties read-only/actions non-invokable ('greyed out')</li>
+<li>Use the <code>disabledXxx()</code> supporting method on <a href="http://isis.apache.org/how-tos/how-to-02-050-How-to-prevent-a-property-from-being-modified.html">properties</a> and <a href="http://isis.apache.org/how-tos/how-to-02-070-How-to-prevent-an-action-from-being-invoked.html">actions</a> to make a property/action disabled according to some imperative rule</li>
+</ul>
+
+<h2>Business rules: Do it!</h2>
+
+<ul>
+<li>Use the <a href="http://isis.apache.org/reference/recognized-annotations/RegEx.html">@Regex</a> annotation on string properties or action parameters to validate strings</li>
+<li>Use the <a href="">@MinLength</a></li>
+</ul>
+
+<p>(or more generally the <a href="">@MustSatisfy</a> annotation) to specify </p>
+
+<p>Use the <code>validateXxx()</code> supporting method on [properties](</p>
+
+<ul>
+<li>disable</li>
+<li>hide</li>
+<li>vaidate</li>
+</ul>
+
+<p>Bookmarkable  </p>
+
+<p>dashboards (home page)</p>
+
+<p>contributed properties/collections</p>
+
+<p>contributed actions</p>
+
+<p>creating objects:
+- domainobjectcontainer</p>
+
+<p>clockservice</p>
+
+<p>.layout.json</p>
+
+<p>view models</p>
+
+<p>Customising the REST API</p>
+
+<p>wicket extensions
+- excel download
+- fullcalendar
+- gmap3</p>
+
+<p>add-ons
+- security
+- command
+- auditing
+- publishing</p>
+
+<p>event bus</p>
+
+<p>RESTful API</p>
+
+<p>composite fixture scripts (a la Estatio)</p>
+
+<h2>Stuff to do...</h2>
+
+<ul>
+<li>rename domain class</li>
+<li>rename domain service</li>
+<li>fixture scripts</li>
+</ul>
+
+<p>Tests</p>
+
+<ul>
+<li>delete the BDD specs</li>
+</ul>
+
+<h2>Dele</h2>
+
+<h2>Rename domain class, domain service, fixtures</h2>
+
+<h4>SimpleObject</h4>
+
+<p>rename to <code>ConferenceSession</code></p>
+
+<h4>SimpleObjects</h4>
+
+<p>rename to <code>ConferenceSessions</code></p>
+
+<h4>SimpleObjectsFixture</h4>
+
+<p>rename to <code>ConferenceSessionsFixture</code></p>
+
+<h4>myapp-webapp/src/main/webapp/WEB-INF/isis.properties</h4>
+
+<pre>
+isis.services = \
+                10:dom.simple.SimpleObjects,\
+                ...
+
+isis.fixtures=fixture.simple.SimpleObjectsFixture
+</pre>
+
+<p>to</p>
+
+<pre>
+isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                ...
+
+isis.fixtures=fixture.simple.ConferenceSessionsFixture
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<pre>
+isisJdoSupport.executeUpdate("delete from \"SimpleObject\"");
+</pre>
+
+<p>to:</p>
+
+<pre>
+isisJdoSupport.executeUpdate("delete from \"ConferenceSession\"");
+</pre>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/OverheadProjector.png</code> -> <code>myapp/dom/src/main/resources/images/ConferenceSession.png</code></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>getId()</li>
+</ul>
+
+<p><em>a "nice to have", but has an effect on the URLs exposed in the REST API</em></p>
+
+<pre>
+public String getId() {
+    return "conferenceSessions";
+</div>
+</pre>
+
+<ul>
+<li><code>iconName()</code></li>
+</ul>
+
+<pre>
+public String iconName() {
+    return "ConferenceSession";
+</div>
+</pre>
+
+<h4>ConferenceSession</h4>
+
+<p><em>a "nice to have", but has an effect on the URLs exposed in the REST API</em></p>
+
+<pre>
+@ObjectType("SESSION")
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>menu service changed to "Conference Sessions"</li>
+<li>tooltip on icon/title changed to "Conference Session"</li>
+<li>icon changed</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-030</code></p>
+
+<h2>Refactor domain class</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>rename <code>name</code> property to <code>sessionTitle</code></li>
+<li>update <code>compareTo()</code></li>
+</ul>
+
+<pre>
+public int compareTo(ConferenceSession other) {
+    return ObjectContracts.compare(this, other, "sessionTitle");
+</div>
+</pre>
+
+<ul>
+<li>review title</li>
+</ul>
+
+<pre>
+@Title
+public String getSessionTitle() {
+    return sessionTitle;
+</div>
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>Better fixture data</li>
+</ul>
+
+<pre>
+private void installObjects() {
+    create("RRRADDD! Apache Isis");
+    create("Best practices for AngularJS");
+    create("Refactor your specs!");
+    create("Why Kotlin?");
+    create("Introduction to the Play Framework");
+    create("Object Oriented Design in the Wild");
+</div>
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>table shows sessionTitle column</li>
+<li>object form shows sessionTitle property</li>
+<li>object page labelled with title</li>
+<li>updated data</li>
+</ul>
+
+<p>also</p>
+
+<ul>
+<li>discuss injected services into fixtures, entities, services etc.</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-040</code></p>
+
+<h2>Add date scalar property</h2>
+
+<h4>ConferenceSession</h4>
+
+<pre>
+// //////////////////////////////////////
+// date (property)
+// //////////////////////////////////////
+
+private LocalDate date;
+
+@javax.jdo.annotations.Persistent
+@javax.jdo.annotations.Column(allowsNull = "true")
+@MemberOrder(sequence = "1")
+public LocalDate getDate() {
+    return date;
+</div>
+
+public void setDate(final LocalDate date) {
+    this.date = date;
+</div>
+</pre>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<pre>
+private ConferenceSession create(final String name) {
+    ...
+    session.setDate(clockService.now().plusDays((int)(Math.random()*5)));
+    ...
+</div>
+</pre>
+
+<p>and</p>
+
+<pre>
+private ClockService clockService;
+
+public final void injectClockService(final ClockService clockService) {
+    this.clockService = clockService;
+</div>
+</pre>
+
+<p>></p>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>now has a date property in title and in form</li>
+<li>can edit, obviously</li>
+<li>is optional</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-050</code></p>
+
+<h2>Add enum scalar property</h2>
+
+<ul>
+<li>type property</li>
+</ul>
+
+<h5>ConferenceSession</h5>
+
+<pre>
+// //////////////////////////////////////
+// Type (property)
+// //////////////////////////////////////
+
+public enum Type {
+    KEYNOTE, ALL_DAY_TUTORIAL, SESSION, OTHER
+</div>
+
+private Type Type;
+@javax.jdo.annotations.Column(allowsNull="false")
+@MemberOrder(sequence = "1")
+public Type getType() {
+    return Type;
+</div>
+public void setType(final Type Type) {
+    this.Type = Type;
+</div>
+</pre>
+
+<h5>ConferenceSessions</h5>
+
+<ul>
+<li>update the <code>create(...)</code> method</li>
+</ul>
+
+<pre>
+public ConferenceSession create(
+        final @Named("Name") String name,
+        final @Named("Type") ConferenceSession.Type type) {
+    final ConferenceSession obj = newTransientInstance(ConferenceSession.class);
+    obj.setSessionTitle(name);
+    obj.setType(type);
+    persistIfNotAlready(obj);
+    return obj;
+</div>
+</pre>
+
+<h5>ConferenceSessionsFixture</h5>
+
+<ul>
+<li>update the fixture data</li>
+</ul>
+
+<pre>
+private void installObjects() {
+    create("RRRADDD! Apache Isis", ConferenceSession.Type.SESSION);
+    create("Best practices for AngularJS", ConferenceSession.Type.SESSION);
+    create("Refactor your specs!", ConferenceSession.Type.SESSION);
+    create("Why Kotlin?", ConferenceSession.Type.OTHER);
+    create("Introduction to the Play Framework", ConferenceSession.Type.SESSION);
+    create("Object Oriented Design in the Wild", ConferenceSession.Type.ALL_DAY_TUTORIAL);
+</div>
+
+private ConferenceSession create(final String name, final Type type) {
+    return simpleObjects.create(name, type);
+</div>
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>list all
+<ul>
+<li>table with new property</li>
+</ul></li>
+<li>show object form</li>
+<li>edit object details
+<ul>
+<li>both are mandatory</li>
+</ul></li>
+<li>create new object</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-060</code></p>
+
+<h2>UI hints</h2>
+
+<h5>`ConferenceSession</h5>
+
+<ul>
+<li>sessionTitle, then type, then date</li>
+</ul>
+
+<pre>
+@MemberOrder(sequence="1")
+public String getSessionTitle() {
+</pre>
+
+<ul>
+<li>then type</li>
+</ul>
+
+<pre>
+@MemberOrder(sequence = "2")
+public Type getType() {
+</pre>
+
+<ul>
+<li>then date (hide in table)</li>
+</ul>
+
+<pre>
+@Hidden(where=Where.ALL_TABLES)
+@MemberOrder(name="Scheduling", sequence = "3")
+public LocalDate getDate() {
+</pre>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>table shows only title, type properties (in that order)</li>
+<li>order form shows
+<ul>
+<li>title, type properties grouped together in the default 'General' group</li>
+<li>date property separately in a new 'Scheduling' group</li>
+</ul></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-070</code></p>
+
+<h2>Add another type (and domain service)</h2>
+
+<h4>Speaker</h4>
+
+<pre><code>package dom.simple;
+
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.VersionStrategy;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.ObjectType;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.util.ObjectContracts;
+
+@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
+@javax.jdo.annotations.DatastoreIdentity(
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
+         column="id")
+@javax.jdo.annotations.Version(
+        strategy=VersionStrategy.VERSION_NUMBER, 
+        column="version")
+@ObjectType("SPEAKER")
+@Bookmarkable
+public class Speaker implements Comparable&lt;Speaker&gt; {
+
+
+    // //////////////////////////////////////
+    // givenName (property)
+    // //////////////////////////////////////
+
+    private String givenName;
+
+    @MemberOrder(sequence = "1")
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @Title(sequence="2", prepend=", ")
+    public String getGivenName() {
+        return givenName;
+    }
+
+    public void setGivenName(final String name) {
+        this.givenName = name;
+    }
+
+
+    // //////////////////////////////////////
+    // familyName (property)
+    // //////////////////////////////////////
+
+    private String familyName;
+
+    @MemberOrder(sequence = "2")
+    @javax.jdo.annotations.Column(allowsNull="false")
+    @Title(sequence="1")
+    public String getFamilyName() {
+        return familyName;
+    }
+
+    public void setFamilyName(final String familyName) {
+        this.familyName = familyName;
+    }
+
+
+    // //////////////////////////////////////
+    // compareTo
+    // //////////////////////////////////////
+
+    @Override
+    public int compareTo(Speaker other) {
+        return ObjectContracts.compare(this, other, "givenName, familyName");
+    }
+
+
+    // //////////////////////////////////////
+    // Injected
+    // //////////////////////////////////////
+
+    @SuppressWarnings("unused")
+    private DomainObjectContainer container;
+    public void injectDomainObjectContainer(final DomainObjectContainer container) {
+        this.container = container;
+    }
+
+}
+</code></pre>
+
+<h4>Speakers</h4>
+
+<pre><code>package dom.simple;
+
+import java.util.List;
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.ActionSemantics.Of;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Named;
+
+public class Speakers extends AbstractFactoryAndRepository {
+
+    @Override
+    public String getId() {
+        return "speakers";
+    }
+
+    public String iconName() {
+        return "Speaker";
+    }
+
+    @Bookmarkable
+    @ActionSemantics(Of.SAFE)
+    @MemberOrder(sequence = "1")
+    public List&lt;Speaker&gt; listAll() {
+        return allInstances(Speaker.class);
+    }
+
+    @MemberOrder(sequence = "2")
+    public Speaker create(
+            final @Named("Given name") String givenName,
+            final @Named("Family name") String familyName) {
+        Speaker speaker = getContainer().newTransientInstance(Speaker.class);
+        speaker.setGivenName(givenName);
+        speaker.setFamilyName(familyName);
+        getContainer().persistIfNotAlready(speaker);
+        return speaker;
+    }
+}
+</code></pre>
+
+<h5>ConferenceSessionsFixture</h5>
+
+<ul>
+<li>install()</p>
+
+<pre><code>isisJdoSupport.executeUpdate("delete from \"Speaker\"");
+</code></pre></li>
+<li>installObjects()</p>
+
+<pre><code>createSpeaker("Dan", "Haywood");
+createSpeaker("Misko", "Hevery");
+createSpeaker("Cyrille", "Martraire");
+createSpeaker("Svetlana", "Haywood");
+createSpeaker("James", "Ward");
+createSpeaker("Jessica", "Kerr");
+</code></pre></li>
+<li>createSpeaker() method:</p>
+
+<pre><code>private Speaker createSpeaker(final String givenName, final String familyName) {
+    return speakers.create(givenName, familyName);
+}
+</code></pre></li>
+<li>and inject new domain service:</p>
+
+<pre><code>private Speakers speakers;
+public final void injectSpeakers(final Speakers speakers) {
+    this.speakers = speakers;
+}
+</code></pre></li>
+</ul>
+
+<h5>isis.properties</h5>
+
+<ul>
+<li>number indicates the ordering in the menu</p>
+
+<pre><code>isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                20:dom.simple.Speakers,\
+</code></pre></li>
+</ul>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/Customer.gif</code> -> <code>myapp/dom/src/main/resources/images/Speaker.gif</code></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>new domain service</li>
+<li>list, create</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-080</code></p>
+
+<h2>Add reference property</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>'speaker' property (of type 'Speaker')</p>
+
+<pre><code>// //////////////////////////////////////
+// speaker (property)
+// //////////////////////////////////////
+
+
+private Speaker speaker;
+
+
+@javax.jdo.annotations.Column(allowsNull="true")
+@MemberOrder(sequence = "3")
+public Speaker getSpeaker() {
+    return speaker;
+}
+
+
+public void setSpeaker(final Speaker speaker) {
+    this.speaker = speaker;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>displays field, no way to associate :-(</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-090</code></p>
+
+<h2>Add autoComplete (and repository queries)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>autoComplete method:</p>
+
+<pre><code>public List&lt;Speaker&gt; autoCompleteSpeaker(String search) {
+    return speakers.findByGivenOrFamilyName(search);
+}
+</code></pre></li>
+<li>inject service:</p>
+
+<pre><code>private Speakers speakers;
+
+
+public final void injectSpeakers(final Speakers speakers) {
+    this.speakers = speakers;
+}
+</code></pre></li>
+</ul>
+
+<h5>Speakers</h5>
+
+<ul>
+<li>finder (hidden because only intended to be called programmatically)</p>
+
+<pre><code>@Hidden
+public List&lt;Speaker&gt; findByGivenOrFamilyName(String search) {
+    return getContainer().allMatches(
+            new QueryDefault&lt;Speaker&gt;(Speaker.class, 
+                    "findByGivenOrFamilyName", 
+                    "givenOrFamilyName", ".*"+search+".*"));
+}
+</code></pre></li>
+</ul>
+
+<h5>Speaker</h5>
+
+<ul>
+<li>Isis configured to use JDO, so add the annotation</p>
+
+<pre><code>@javax.jdo.annotations.Queries({
+    @javax.jdo.annotations.Query(
+            name="findByGivenOrFamilyName", language="JDOQL",
+            value="SELECT "
+                    + "FROM dom.simple.Speaker "
+                    + "WHERE givenName.matches(:givenOrFamilyName) "
+                    + "   || familyName.matches(:givenOrFamilyName)")
+})
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can now add speaker
+<ul>
+<li>using either given or family name</li>
+</ul></li>
+<li>Speakers finder is not visible</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-100</code></p>
+
+<h2>Update title (also, prototype actions)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>title imperatively, rather than declaratively</p>
+
+<pre><code>public String title() {
+    final TitleBuffer buf = new TitleBuffer();
+    if(getSessionTitle().length()&gt;20) {
+        buf.append(getSessionTitle().substring(0, 20)).append("...");
+    } else {
+        buf.append(getSessionTitle());
+    }
+    if(getSpeaker() != null) {
+        buf.append("(").append(container.titleOf(getSpeaker())).append(")");
+    }
+    return buf.toString();
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>metamodel validation exception</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>remove <code>@Title</code> from <code>#sessionTitle</code></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>Add prototyping action to domain service</p>
+
+<pre><code>@Bookmarkable
+@Prototype
+@ActionSemantics(Of.SAFE)
+public ConferenceSession firstOne() {
+    return listAll().get(0);
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>now runs ok</li>
+<li>prototype action styled differently</li>
+<li>add speaker to session</li>
+<li>title changes</li>
+<li>(optional) run in SERVER mode</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-110</code></p>
+
+<h2>Another new entity, Tag</h2>
+
+<p><em>the motivation for this is that we want to add a new collection of tags from ConferenceSession</em></p>
+
+<h4>Tag</h4>
+
+<ul>
+<li>new entity (note the <code>compareTo</code>, <code>toString</code>, using Isis helper methods)</p>
+
+<pre><code>package dom.simple;
+
+
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.VersionStrategy;
+
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.annotation.Bookmarkable;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.ObjectType;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.util.ObjectContracts;
+
+
+@javax.jdo.annotations.PersistenceCapable(identityType=IdentityType.DATASTORE)
+@javax.jdo.annotations.DatastoreIdentity(
+        strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY,
+         column="id")
+@javax.jdo.annotations.Version(
+        strategy=VersionStrategy.VERSION_NUMBER, 
+        column="version")
+@ObjectType("TAG")
+@Bookmarkable
+public class Tag implements Comparable&lt;Tag&gt; {
+
+
+<pre><code>// //////////////////////////////////////
+// name (property)
+// //////////////////////////////////////
+
+
+private String name;
+
+
+@javax.jdo.annotations.Column(allowsNull = "true")
+@Title
+@MemberOrder(sequence = "1")
+public String getName() {
+    return name;
+}
+
+
+public void setName(final String name) {
+    this.name = name;
+}
+
+
+// //////////////////////////////////////
+// compareTo
+// //////////////////////////////////////
+
+
+@Override
+public int compareTo(Tag other) {
+    return ObjectContracts.compare(this, other, "name");
+}
+
+
+// //////////////////////////////////////
+// toString
+// //////////////////////////////////////
+
+
+@Override
+public String toString() {
+    return ObjectContracts.toString(this, "name");
+}
+
+
+// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+@SuppressWarnings("unused")
+private DomainObjectContainer container;
+public void injectDomainObjectContainer(final DomainObjectContainer container) {
+    this.container = container;
+}
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>Tags</h4>
+
+<ul>
+<li>new domain service; all actions are hidden</p>
+
+<pre><code>package dom.simple;
+
+
+import java.util.List;
+
+
+import org.apache.isis.applib.AbstractFactoryAndRepository;
+import org.apache.isis.applib.annotation.Hidden;
+
+
+public class Tags extends AbstractFactoryAndRepository {
+
+
+<pre><code>@Override
+public String getId() {
+    return "tags";
+}
+public String iconName() {
+    return "Tag";
+}
+
+
+@Hidden
+public List&amp;lt;Tag&amp;gt; listAll() {
+    return allInstances(Tag.class);
+}
+@Hidden
+public Tag create(final String name) {
+    final Tag obj = newTransientInstance(Tag.class);
+    obj.setName(name);
+    persistIfNotAlready(obj);
+    return obj;
+}
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>installObjects()</p>
+
+<pre><code>createTag("UX");
+createTag("Mobile");
+createTag("Java");
+createTag("Agile");
+createTag(".NET");
+createTag("Test");
+createTag("Mastery");
+createTag("Web");
+createTag("Architecture");
+createTag("Dev Ops");
+createTag("Cloud");
+createTag("Languages");
+createTag("Tools");
+createTag("Team");
+createTag("Database");
+createTag("Javascript");
+createTag("Keynote");
+</code></pre></li>
+<li>createTag() method</p>
+
+<pre><code>private Tag createTag(final String name) {
+    return tags.create(name);
+}
+</code></pre></li>
+<li>inject new service</p>
+
+<pre><code>private Tags tags;
+public final void injectTags(final Tags tags) {
+    this.tags = tags;
+}
+</code></pre></li>
+</ul>
+
+<h4>isis.properties</h4>
+
+<ul>
+<li>no number required, since not visible in UI</p>
+
+<pre><code>isis.services = \
+                10:dom.simple.ConferenceSessions,\
+                20:dom.simple.Speakers,\
+                dom.simple.Tags,\
+</code></pre></li>
+</ul>
+
+<h4>icon</h4>
+
+<ul>
+<li>add icon, <code>icons/RedFlag.png</code> -> <code>myapp/dom/src/main/resources/images/Tag.png</code></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>(no visible change, since Tags actions are all hidden)                </li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-120</code></p>
+
+<h2>Add collection</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>tags collection</p>
+
+<pre><code>// //////////////////////////////////////
+// tags (collection)
+// //////////////////////////////////////
+
+
+@javax.jdo.annotations.Join
+@javax.jdo.annotations.Element(dependent = "false")
+private SortedSet&lt;Tag&gt; tags = new TreeSet&lt;Tag&gt;();
+
+
+@Render(Render.Type.EAGERLY)
+@Disabled
+@MemberOrder(sequence = "1")
+public SortedSet&lt;Tag&gt; getTags() {
+    return tags;
+}
+
+
+public void setTags(final SortedSet&lt;Tag&gt; tags) {
+    this.tags = tags;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view the DDL</li>
+<li>open object</li>
+<li>can't add tags :-(</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-130</code></p>
+
+<h2>Add actions</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>addTag action</p>
+
+<pre><code>// //////////////////////////////////////
+// addTag (action)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "1")
+public ConferenceSession addTag(final Tag tag) {
+    getTags().add(tag);
+    return this;
+}
+</code></pre></li>
+<li>removeTag action</p>
+
+<pre><code>// //////////////////////////////////////
+// removeTag (action)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "2")
+public ConferenceSession removeTag(final Tag tag) {
+    getTags().remove(tag);
+    return this;
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>create(...) method, add three (random) tags for each session</p>
+
+<pre><code>for (Tag tag : random(tags.listAll(), 3)) {
+    session.addTag(tag);
+}
+</code></pre></li>
+<li>supporting random(...) method</p>
+
+<pre><code>private List&lt;Tag&gt; random(List&lt;Tag&gt; tags, int num) {
+    List&lt;Tag&gt; availableTags = Lists.newArrayList(tags);
+    List&lt;Tag&gt; selectedTags = Lists.newArrayList();
+    while(selectedTags.size()&lt;num) {
+        int selected = (int)(availableTags.size() * Math.random());
+        try {
+            selectedTags.add(availableTags.remove(selected));
+        } catch(Exception ex) {}
+    }
+    return selectedTags;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>view</li>
+<li>need to get a reference to the tag again...</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-140</code></p>
+
+<h2>Add choices</h2>
+
+<p><em>an alternative to using autoComplete, useful if the list of choices is relatively short</em></p>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>inject Tags repo</p>
+
+<pre><code>private Tags tagRepo;
+
+
+public final void injectTags(final Tags tags) {
+    this.tagRepo = tags;
+}
+</code></pre></li>
+<li>added the "choices" supporting method for addTag action (all tags not yet added)</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    tags.removeAll(getTags());
+    return tags;
+}
+</code></pre></li>
+<li>added the "choices" supporting method for removeTag action (only those tags previously added)</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0RemoveTag() {
+    return getTags();
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can now add/remove tags, yay!</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-150</code></p>
+
+<h2>More UI Hints (dynamic, this time)</h2>
+
+<h4>DEMO:</h4>
+
+<ul>
+<li>download layout</li>
+<li>edit column spans</li>
+<li>copy <code>ConferenceSession.layout.json</code> (below) to <code>myapp-dom/src/main/java/dom/simple</code></li>
+<li>refresh layout</p>
+
+<pre><code>{
+  "columns": [
+    {
+      "span": 6,
+      "memberGroups": {
+        "General": {
+          "members": {
+            "sessionTitle": {},
+            "type": {},
+            "speaker": {}
+          }
+        },
+        "Scheduling": {
+          "members": {
+            "date": {}
+          }
+        }
+      }
+    },
+    {
+      "span": 0,
+      "memberGroups": {}
+    },
+    {
+      "span": 0,
+      "memberGroups": {}
+    },
+    {
+      "span": 6,
+      "collections": {
+        "tags": {
+          "actions": {
+            "addTag": {},
+            "removeTag": {}
+          }
+        }
+      }
+    }
+  ],
+  "actions": {
+    "downloadLayout": {},
+    "refreshLayout": {}
+  }
+}
+</code></pre></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-160</code></p>
+
+<h2>Declarative and imperative business rules</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>add RegEx pattern and a maximum length to the 'sessionTitle' property (latter using JDO annotation)</p>
+
+<pre><code>@javax.jdo.annotations.Column(allowsNull="false",length=40)
+@RegEx(validation="[^%]+")
+public String getSessionTitle() {
+</code></pre></li>
+<li>can't add more than 4 tags</p>
+
+<pre><code>public String disableAddTag(Tag tag) {
+    return getTags().size() &gt;= 4? "Cannot add more than 4 tags": null;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>cannot save session whose sessionTitle property has > 40 chars</li>
+<li>cannot add more than 4 tags to a session</li>
+* 
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-170</code></p>
+
+<h2>Other business rules (hiding, validation)</h2>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>hideAddTag </p>
+
+<pre><code>public boolean hideAddTag() {
+    return getTags().size() &gt;= 4;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>action becomes hidden</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>offer tags that are already in the collection, but then prevent using validation</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    //tags.removeAll(getTags());
+    return tags;
+}
+
+
+public String validateAddTag(Tag tag) {
+    return getTags().contains(tag)? "Already added that tag": null;
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>can attempt to add a tag, but prevented</li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>backing out the above changes</p>
+
+<pre><code>public Collection&lt;Tag&gt; choices0AddTag() {
+    List&lt;Tag&gt; tags = Lists.newArrayList(tagRepo.listAll());
+    tags.removeAll(getTags());
+    return tags;
+}
+
+
+//    public String validateAddTag(Tag tag) {
+//        return getTags().contains(tag)? "Already added that tag": null;
+//    }
+
+
+//    public boolean hideAddTag() {
+//        return getTags().size() &gt;= 4;
+//    }
+
+
+public String disableAddTag(Tag tag) {
+    return getTags().size() &gt;= 4? "Cannot add more than 4 tags": null;
+}
+</code></pre></li>
+</ul>
+
+<h2>Contributed Actions etc</h2>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>add action, will be contributed to Tag, as both action and a collection </p>
+
+<pre><code>// //////////////////////////////////////
+// Contributions
+// //////////////////////////////////////
+
+
+// @NotContributed
+@NotInServiceMenu
+public List&lt;ConferenceSession&gt; findByTag(Tag tag) {
+    return allMatches(new QueryDefault&lt;ConferenceSession&gt;(
+            ConferenceSession.class, "findByTag", "tag", tag));
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>declare JDO query</p>
+
+<pre><code>@javax.jdo.annotations.Queries({
+    @javax.jdo.annotations.Query(
+            name="findByTag", language="JDOQL",
+            value="SELECT "
+                    + "FROM dom.simple.ConferenceSession "
+                    + "WHERE tags.contains(:tag)")
+})
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view Tag; shows the sessions that are associated with it</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-180</code></p>
+
+<h2>Bookmarks</h2>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>bookmarked objects
+<ul>
+<li>eg <code>ConferenceSession</code></li>
+</ul></li>
+<li>bookmarked actions
+<ul>
+<li>eg <code>ConferenceSessions#listAll</code></li>
+</ul></li>
+</ul>
+
+<h2>CSS</h2>
+
+<h4>myapp-webapp/src/main/webapp/css/application.css</h4>
+
+<ul>
+<li>example custom CSS already defined for "x-highlight" and "x-caution" CSS classes </li>
+</ul>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>addTag, add annotation</p>
+
+<pre><code>@CssClass("x-highlight")
+public ConferenceSession addTag(final Tag tag) {
+</code></pre></li>
+<li>removeTag, add annotation</p>
+
+<pre><code>@CssClass("x-caution")
+public ConferenceSession removeTag(final Tag tag) {
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>view session object</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-190</code></p>
+
+<h2>Widgets, also @HomePage</h2>
+
+<p><em>show Conference sessions on a calendar view.  This uses a "third-party" component, up on github</em></p>
+
+<h4>myapp/pom.xml</h4>
+
+<ul>
+<li>parent module, scope=import for dependency management</p>
+
+<pre><code>&lt;dependencyManagement&gt;
+    &lt;dependencies&gt;
+        ...
+
+
+<pre><code>    &amp;lt;dependency&amp;gt;
+        &amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+        &amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2&amp;lt;/artifactId&amp;gt;
+        &amp;lt;version&amp;gt;1.3.0&amp;lt;/version&amp;gt;
+        &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
+        &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
+    &amp;lt;/dependency&amp;gt;
+
+
+&amp;lt;/dependencies&amp;gt;
+</code></pre>
+
+&lt;/dependencyManagement&gt;
+</code></pre></li>
+</ul>
+
+<h4>myapp-dom/pom.xml</h4>
+
+<ul>
+<li>dom project depends on modules 'applib' (minimal coupling, defines an interface and value type)</p>
+
+<pre><code>&lt;dependencies&gt;
+    ...
+
+
+<pre><code>&amp;lt;dependency&amp;gt;
+    &amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+    &amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2-applib&amp;lt;/artifactId&amp;gt;
+&amp;lt;/dependency&amp;gt;
+</code></pre>
+
+&lt;/dependencies&gt;
+</code></pre></li>
+</ul>
+
+<h4>myapp-webapp/pom.xml</h4>
+
+<ul>
+<li>webapp project references the actual widget UI implementation</p>
+
+<pre><code>&lt;dependencies&gt;
+    ...
+
+
+<pre><code>&amp;lt;dependency&amp;gt;
+   &amp;lt;groupId&amp;gt;com.danhaywood.isis.wicket&amp;lt;/groupId&amp;gt;
+    &amp;lt;artifactId&amp;gt;danhaywood-isis-wicket-fullcalendar2-ui&amp;lt;/artifactId&amp;gt;
+&amp;lt;/dependency&amp;gt;
+</code></pre>
+
+&lt;/dependencies&gt;
+</code></pre></li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-200</code></p>
+
+<h4>ConferenceSession</h4>
+
+<ul>
+<li>declare it implements the <code>CalendarEventable</code> interface</p>
+
+<pre><code>public class ConferenceSession implements Comparable&lt;ConferenceSession&gt;
+    , com.danhaywood.isis.wicket.fullcalendar2.applib.CalendarEventable {
+</code></pre></li>
+<li>and implement the required methods:</p>
+
+<pre><code>    // //////////////////////////////////////
+    // CalendarEventable impl
+    // //////////////////////////////////////
+
+
+<pre><code>@Programmatic
+@Override
+public String getCalendarName() {
+    return "date";
+}
+
+
+@Programmatic
+@Override
+public CalendarEvent toCalendarEvent() {
+    return new CalendarEvent(
+            getDate().toDateTimeAtStartOfDay(), 
+            "date", 
+            container.titleOf(this));
+}
+</code></pre>
+
+</code></pre></li>
+</ul>
+
+<h2>ConferenceSessions</h2>
+
+<ul>
+<li>indicate that the listAll action should be called for the home page</p>
+
+<pre><code>@HomePage
+@Bookmarkable
+@ActionSemantics(Of.SAFE)
+@MemberOrder(sequence = "1")
+public List&lt;ConferenceSession&gt; listAll() {
+    return allInstances(ConferenceSession.class);
+}
+</code></pre></li>
+</ul>
+
+<h4>DEMO</h4>
+
+<ul>
+<li>all sessions displayed automatically on home page</li>
+<li>switch to calendar view (top right, new icon should appear)</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-210</code></p>
+
+<h2>View models</h2>
+
+<h4>SpeakerViewModel</h4>
+
+<ul>
+<li>shows summary info of speaker, their sessions, the tags of those sessions</p>
+
+<pre><code>package dom.simple;
+
+
+import java.util.List;
+import java.util.Set;
+
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.ViewModel;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Render;
+import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.BaseEncoding;
+
+
+public class SpeakerViewModel implements ViewModel {
+
+
+<pre><code>// //////////////////////////////////////
+// ViewModel impl
+// //////////////////////////////////////
+
+
+@Override
+public String viewModelMemento() {
+    Bookmark bookmark = bookmarkService.bookmarkFor(getSpeaker());
+    return encode(bookmark);
+}
+
+
+@Override
+public void viewModelInit(String memento) {
+    Bookmark bookmark = decode(memento);
+    setSpeaker((Speaker) bookmarkService.lookup(bookmark));
+}
+
+
+static String encode(Bookmark bookmark) {
+    return BaseEncoding.base32().encode(bookmark.toString().getBytes());
+}
+
+
+private static Bookmark decode(String memento) {
+    return new Bookmark(new String(BaseEncoding.base32().decode(memento)));
+}
+
+
+// //////////////////////////////////////
+// speaker (property)
+// //////////////////////////////////////
+
+
+private Speaker speaker;
+
+
+@javax.jdo.annotations.Column(allowsNull = "true")
+@Title
+@MemberOrder(sequence = "1")
+public Speaker getSpeaker() {
+    return speaker;
+}
+
+
+public void setSpeaker(final Speaker speaker) {
+    this.speaker = speaker;
+}
+
+
+// //////////////////////////////////////
+// numberOfSessions (property)
+// //////////////////////////////////////
+
+
+@MemberOrder(sequence = "2")
+public int getNumberOfSessions() {
+    return getPresenting().size();
+}
+
+
+// //////////////////////////////////////
+// presenting (collection)
+// //////////////////////////////////////
+
+
+@Render(Type.EAGERLY)
+@MemberOrder(sequence = "1")
+public List&amp;lt;ConferenceSession&amp;gt; getPresenting() {
+    return container.allMatches(ConferenceSession.class, new Predicate&amp;lt;ConferenceSession&amp;gt;() {
+        @Override
+        public boolean apply(ConferenceSession input) {
+            return input.getSpeaker() == getSpeaker();
+        }
+    });
+}
+
+
+// //////////////////////////////////////
+// tags (collection)
+// //////////////////////////////////////
+
+
+@Render(Type.EAGERLY)
+@MemberOrder(sequence = "2")
+public List&amp;lt;Tag&amp;gt; getTags() {
+    return Lists.newArrayList(
+            Iterables.concat(
+                Iterables.transform(
+                    getPresenting(), 
+                    new Function&amp;lt;ConferenceSession, Set&amp;lt;Tag&amp;gt;&amp;gt;(){
+                        @Override
+                        public Set&amp;lt;Tag&amp;gt; apply(ConferenceSession input) {
+                            return input.getTags();
+                        }
+                    })
+            ));
+}
+
+
+// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+private DomainObjectContainer container;
+public final void injectDomainObjectContainer(final DomainObjectContainer container) {
+    this.container = container;
+}
+
+
+private BookmarkService bookmarkService;
+public final void injectBookmarkService(final BookmarkService bookmarkService) {
+    this.bookmarkService = bookmarkService;
+}  
+</code></pre>
+
+}
+</code></pre></li>
+</ul>
+
+<h4>Speakers</h4>
+
+<ul>
+<li>new action to return all speakers as home page (should really be in its own application service...)</p>
+
+<pre><code>// //////////////////////////////////////
+// view models
+// //////////////////////////////////////
+
+
+@ActionSemantics(Of.SAFE)
+@HomePage
+public List&lt;SpeakerViewModel&gt; all() {
+    return Lists.newArrayList(Iterables.transform(listAll(), 
+        new Function&lt;Speaker, SpeakerViewModel&gt;() {
+            @Override
+            public SpeakerViewModel apply(Speaker input) {
+                Bookmark bookmark = bookmarkService.bookmarkFor(input);
+                return getContainer().newViewModelInstance(
+                        SpeakerViewModel.class, 
+                        SpeakerViewModel.encode(bookmark));
+            }
+        })
+    );
+}
+</code></pre></li>
+<li>and inject in the framework-provided <code>BookmarkService</code>    </p>
+
+<pre><code>// //////////////////////////////////////
+// Injected
+// //////////////////////////////////////
+
+
+private BookmarkService bookmarkService;
+
+
+public final void injectBookmarkService(final BookmarkService bookmarkService) {
+    this.bookmarkService = bookmarkService;
+}
+</code></pre></li>
+</ul>
+
+<h4>ConferenceSessions</h4>
+
+<ul>
+<li>remove @HomePage annotation from the earlier <code>listAll()</code> action</li>
+</ul>
+
+<h4>ConferenceSessionsFixture</h4>
+
+<ul>
+<li>create(...) set up random speaker with each session</p>
+
+<pre><code>session.setSpeaker(randomSpeaker());
+</code></pre></li>
+<li>randomSpeaker() supporting method</p>
+
+<pre><code>private Speaker randomSpeaker() {
+    List&lt;Speaker&gt; all = speakers.listAll();
+    while(true) {
+        try {
+            int selected = (int)(all.size() * Math.random());
+            return all.get(selected);
+        } catch(Exception ex){}
+    }
+}
+</code></pre></li>
+</ul>
+
+<h4>Demo</h4>
+
+<ul>
+<li>hit the home page</li>
+<li>select a speaker view model</li>
+</ul>
+
+<h5>Checkpoint:</h5>
+
+<p><code>git reset --hard checkpoint-220</code></p>
+
+<h2>REST API</h2>
+
+<h4>DEMO</h4>
+
+<ul>
+<li><a href="http://localhost:8080/restful">http://localhost:8080/restful</a></li>
+</ul>
+
+
+
+      </div>
+
+        <div id="edit" class="modal hide fade in" style="xxxdisplay: none; ">
+            <div class="modal-header">
+                <a class="close" data-dismiss="modal">x</a>
+
+                <h3>Thank you for contributing to the documention!</h3>
+            </div>
+            <div class="modal-body">
+                <h4>Any help with the documentation is greatly appreciated.</h4>
+                <p>All edits are reviewed before going live, so feel free to do much more than fix typos or links.  If you see a page that could benefit from an entire rewrite, we'd be thrilled to review it.  Don't be surprised if we like it so much we ask you for help with other pages :)</p>
+                <small>NOTICE: unless indicated otherwise on the pages in question, all editable content available from apache.org is presumed to be licensed under the Apache License (AL) version 2.0 and hence all submissions to apache.org treated as formal Contributions under the license terms.</small>
+                <!--[if gt IE 6]>
+                <h4>Internet Explorer Users</h4>
+                <p>If you are not an Apache committer, click the Yes link and enter a <i>anonymous</i> for the username and leave the password empty</p>
+                <![endif]-->
+
+            </div>
+            <div class="modal-footer">
+                Do you have an Apache ID?
+                <a href="javascript:void(location.href='https://cms.apache.org/redirect?uri='+escape(location.href))" class="btn">Yes</a>
+                <a href="javascript:void(location.href='https://anonymous:@cms.apache.org/redirect?uri='+escape(location.href))" class="btn">No</a>
+            </div>
+        </div>
+        <!--
+        <script src="./../javascript/bootstrap-modal.js"></script>
+        -->
+
+      <footer>
+        <hr/>
+        <p>
+        Copyright &copy; 2010~2014 The Apache Software Foundation, Licensed under the Apache License, Version 2.0.
+        <br/>
+        Apache Isis, Isis, Apache, the Apache feather logo, and the Apache Isis project logo are trademarks of The Apache Software Foundation.
+        </p>
+      </footer>
+
+    </div> <!-- /container -->
+
+  </body>
+</html>